"use client";

import type { KeyboardEvent } from "react";
import { useState } from "react";
import styles from "./CalculatorWidget.module.css";

type Operator = "add" | "subtract" | "multiply" | "divide";

type CalculatorButtonProps = {
  label: string;
  ariaLabel?: string;
  className?: string;
  onClick: () => void;
};

const maxDisplayLength = 16;

function parseDisplay(value: string): number {
  const normalizedValue = value.replace(",", ".");
  const numberValue = Number(normalizedValue);

  if (!Number.isFinite(numberValue)) {
    return 0;
  }

  return numberValue;
}

function formatNumber(value: number): string {
  if (!Number.isFinite(value)) {
    return "Fehler";
  }

  if (Object.is(value, -0)) {
    return "0";
  }

  const absoluteValue = Math.abs(value);

  if (absoluteValue !== 0 && (absoluteValue >= 1e15 || absoluteValue < 1e-9)) {
    return value.toExponential(8).replace(".", ",");
  }

  const roundedValue = Math.round(value * 1e10) / 1e10;

  return roundedValue.toLocaleString("de-DE", {
    useGrouping: false,
    maximumFractionDigits: 10
  });
}

function calculate(firstValue: number, secondValue: number, operator: Operator): number {
  if (operator === "add") {
    return firstValue + secondValue;
  }

  if (operator === "subtract") {
    return firstValue - secondValue;
  }

  if (operator === "multiply") {
    return firstValue * secondValue;
  }

  if (operator === "divide") {
    if (secondValue === 0) {
      return Number.NaN;
    }

    return firstValue / secondValue;
  }

  return secondValue;
}

function getOperatorLabel(operator: Operator | null): string {
  if (operator === "add") {
    return "+";
  }

  if (operator === "subtract") {
    return "−";
  }

  if (operator === "multiply") {
    return "×";
  }

  if (operator === "divide") {
    return "÷";
  }

  return "";
}

function CalculatorButton({ label, ariaLabel, className, onClick }: CalculatorButtonProps) {
  return (
    <button
      type="button"
      className={[styles.button, className].filter(Boolean).join(" ")}
      aria-label={ariaLabel ?? label}
      onClick={onClick}
    >
      {label}
    </button>
  );
}

export default function CalculatorWidget() {
  const [display, setDisplay] = useState("0");
  const [storedValue, setStoredValue] = useState<number | null>(null);
  const [pendingOperator, setPendingOperator] = useState<Operator | null>(null);
  const [waitingForOperand, setWaitingForOperand] = useState(false);
  const [memory, setMemory] = useState(0);

  const displayIsError = display === "Fehler";

  function resetIfError(): boolean {
    if (!displayIsError) {
      return false;
    }

    setDisplay("0");
    setStoredValue(null);
    setPendingOperator(null);
    setWaitingForOperand(false);

    return true;
  }

  function inputDigit(digit: string) {
    if (resetIfError()) {
      setDisplay(digit);
      return;
    }

    if (waitingForOperand) {
      setDisplay(digit);
      setWaitingForOperand(false);
      return;
    }

    setDisplay((currentDisplay) => {
      if (currentDisplay === "0") {
        return digit;
      }

      if (currentDisplay.replace("-", "").replace(",", "").length >= maxDisplayLength) {
        return currentDisplay;
      }

      return `${currentDisplay}${digit}`;
    });
  }

  function inputDecimal() {
    if (resetIfError()) {
      setDisplay("0,");
      return;
    }

    if (waitingForOperand) {
      setDisplay("0,");
      setWaitingForOperand(false);
      return;
    }

    setDisplay((currentDisplay) => {
      if (currentDisplay.includes(",")) {
        return currentDisplay;
      }

      return `${currentDisplay},`;
    });
  }

  function clearEntry() {
    setDisplay("0");
    setWaitingForOperand(false);
  }

  function clearAll() {
    setDisplay("0");
    setStoredValue(null);
    setPendingOperator(null);
    setWaitingForOperand(false);
  }

  function backspace() {
    if (resetIfError() || waitingForOperand) {
      setDisplay("0");
      setWaitingForOperand(false);
      return;
    }

    setDisplay((currentDisplay) => {
      if (currentDisplay.length <= 1 || (currentDisplay.length === 2 && currentDisplay.startsWith("-"))) {
        return "0";
      }

      return currentDisplay.slice(0, -1);
    });
  }

  function toggleSign() {
    if (resetIfError()) {
      return;
    }

    setDisplay((currentDisplay) => {
      if (currentDisplay === "0") {
        return currentDisplay;
      }

      return currentDisplay.startsWith("-") ? currentDisplay.slice(1) : `-${currentDisplay}`;
    });
  }

  function applyUnary(operation: "percent" | "reciprocal" | "square" | "sqrt") {
    if (resetIfError()) {
      return;
    }

    const currentValue = parseDisplay(display);
    let nextValue = currentValue;

    if (operation === "percent") {
      if (storedValue !== null && pendingOperator) {
        nextValue = (storedValue * currentValue) / 100;
      } else {
        nextValue = currentValue / 100;
      }
    }

    if (operation === "reciprocal") {
      nextValue = currentValue === 0 ? Number.NaN : 1 / currentValue;
    }

    if (operation === "square") {
      nextValue = currentValue * currentValue;
    }

    if (operation === "sqrt") {
      nextValue = currentValue < 0 ? Number.NaN : Math.sqrt(currentValue);
    }

    setDisplay(formatNumber(nextValue));
    setWaitingForOperand(true);
  }

  function chooseOperator(operator: Operator) {
    if (resetIfError()) {
      return;
    }

    const currentValue = parseDisplay(display);

    if (storedValue === null) {
      setStoredValue(currentValue);
    } else if (pendingOperator && !waitingForOperand) {
      const result = calculate(storedValue, currentValue, pendingOperator);
      setDisplay(formatNumber(result));
      setStoredValue(result);
    }

    setPendingOperator(operator);
    setWaitingForOperand(true);
  }

  function applyEquals() {
    if (resetIfError()) {
      return;
    }

    if (storedValue === null || pendingOperator === null) {
      return;
    }

    const currentValue = parseDisplay(display);
    const result = calculate(storedValue, currentValue, pendingOperator);

    setDisplay(formatNumber(result));
    setStoredValue(null);
    setPendingOperator(null);
    setWaitingForOperand(true);
  }

  function memoryClear() {
    setMemory(0);
  }

  function memoryRecall() {
    setDisplay(formatNumber(memory));
    setWaitingForOperand(true);
  }

  function memoryAdd() {
    if (resetIfError()) {
      return;
    }

    setMemory((currentMemory) => currentMemory + parseDisplay(display));
    setWaitingForOperand(true);
  }

  function memorySubtract() {
    if (resetIfError()) {
      return;
    }

    setMemory((currentMemory) => currentMemory - parseDisplay(display));
    setWaitingForOperand(true);
  }

  function memoryStore() {
    if (resetIfError()) {
      return;
    }

    setMemory(parseDisplay(display));
    setWaitingForOperand(true);
  }

  function handleKeyboard(event: KeyboardEvent<HTMLDivElement>) {
    const key = event.key;
    const code = event.code;
    let handled = true;

    if (/^[0-9]$/.test(key)) {
      inputDigit(key);
    } else if (key === "," || key === "." || code === "NumpadDecimal") {
      inputDecimal();
    } else if (key === "+" || code === "NumpadAdd") {
      chooseOperator("add");
    } else if (key === "-" || code === "NumpadSubtract") {
      chooseOperator("subtract");
    } else if (key === "*" || code === "NumpadMultiply") {
      chooseOperator("multiply");
    } else if (key === "/" || code === "NumpadDivide") {
      chooseOperator("divide");
    } else if (key === "Enter" || key === "=" || code === "NumpadEnter") {
      applyEquals();
    } else if (key === "Backspace") {
      backspace();
    } else if (key === "Escape") {
      clearAll();
    } else if (key === "Delete") {
      clearEntry();
    } else if (key === "%") {
      applyUnary("percent");
    } else if (key === "F9") {
      toggleSign();
    } else {
      handled = false;
    }

    if (handled) {
      event.preventDefault();
      event.stopPropagation();
    }
  }

  return (
    <div
      className={`${styles.calculator} widgetNoDrag`}
      tabIndex={0}
      role="application"
      aria-label="Taschenrechner"
      onKeyDown={handleKeyboard}
    >
      <output className={styles.display} aria-label="Anzeige">
        {display}
      </output>

      <div className={styles.memoryRow} aria-label="Speicherfunktionen">
        <button type="button" className={styles.memoryButton} onClick={memoryClear} disabled={memory === 0}>
          MC
        </button>
        <button type="button" className={styles.memoryButton} onClick={memoryRecall} disabled={memory === 0}>
          MR
        </button>
        <button type="button" className={styles.memoryButton} onClick={memoryAdd}>
          M+
        </button>
        <button type="button" className={styles.memoryButton} onClick={memorySubtract}>
          M−
        </button>
        <button type="button" className={styles.memoryButton} onClick={memoryStore}>
          MS
        </button>
        <button type="button" className={styles.memoryButton} onClick={memoryRecall} disabled={memory === 0}>
          M
        </button>
      </div>

      <div className={styles.keypad}>
        <CalculatorButton label="%" className={styles.utilityButton} onClick={() => applyUnary("percent")} />
        <CalculatorButton label="CE" className={styles.utilityButton} onClick={clearEntry} />
        <CalculatorButton label="C" className={styles.utilityButton} onClick={clearAll} />
        <CalculatorButton label="⌫" ariaLabel="Rückschritt" className={styles.utilityButton} onClick={backspace} />

        <CalculatorButton label="1/x" className={styles.utilityButton} onClick={() => applyUnary("reciprocal")} />
        <CalculatorButton label="x²" className={styles.utilityButton} onClick={() => applyUnary("square")} />
        <CalculatorButton label="²√x" className={styles.utilityButton} onClick={() => applyUnary("sqrt")} />
        <CalculatorButton label="÷" className={styles.operatorButton} onClick={() => chooseOperator("divide")} />

        <CalculatorButton label="7" className={styles.numberButton} onClick={() => inputDigit("7")} />
        <CalculatorButton label="8" className={styles.numberButton} onClick={() => inputDigit("8")} />
        <CalculatorButton label="9" className={styles.numberButton} onClick={() => inputDigit("9")} />
        <CalculatorButton label="×" className={styles.operatorButton} onClick={() => chooseOperator("multiply")} />

        <CalculatorButton label="4" className={styles.numberButton} onClick={() => inputDigit("4")} />
        <CalculatorButton label="5" className={styles.numberButton} onClick={() => inputDigit("5")} />
        <CalculatorButton label="6" className={styles.numberButton} onClick={() => inputDigit("6")} />
        <CalculatorButton label="−" className={styles.operatorButton} onClick={() => chooseOperator("subtract")} />

        <CalculatorButton label="1" className={styles.numberButton} onClick={() => inputDigit("1")} />
        <CalculatorButton label="2" className={styles.numberButton} onClick={() => inputDigit("2")} />
        <CalculatorButton label="3" className={styles.numberButton} onClick={() => inputDigit("3")} />
        <CalculatorButton label="+" className={styles.operatorButton} onClick={() => chooseOperator("add")} />

        <CalculatorButton label="+/−" className={styles.numberButton} onClick={toggleSign} />
        <CalculatorButton label="0" className={styles.numberButton} onClick={() => inputDigit("0")} />
        <CalculatorButton label="," className={styles.numberButton} onClick={inputDecimal} />
        <CalculatorButton
          label="="
          ariaLabel={`Gleich ${getOperatorLabel(pendingOperator)}`}
          className={styles.equalsButton}
          onClick={applyEquals}
        />
      </div>
    </div>
  );
}
