"use client";

import { useEffect, useRef, useState, type CSSProperties } from "react";
import { RepaymentSummary } from "@/components/repayment-summary";
import {
  calculateLoan,
  calculatorSchema,
  editLoanAmount,
  normalizeLoanAmount,
  parseLoanAmount,
} from "@/lib/calculator";
export function LoanCalculator() {
  const [amount, setAmount] = useState("25,000");
  const [months, setMonths] = useState("36");
  const [rate, setRate] = useState(2);
  const [result, setResult] = useState(() => ({
    months: 36,
    rate: 2,
    ...calculateLoan({ amount: 25000, months: 36, rate: 2 }),
  }));
  const [error, setError] = useState("");
  const pendingUpdate = useRef<ReturnType<typeof setTimeout> | undefined>(
    undefined,
  );
  useEffect(() => () => clearTimeout(pendingUpdate.current), []);
  const [composingAmount, setComposingAmount] = useState<string | null>(null);
  const amountInput = useRef<HTMLInputElement>(null);
  const amountHistory = useRef({
    entries: [{ amount: "25,000", caret: 7 }],
    index: 0,
  });
  const collapsedBeforeInput = useRef(true);
  const stepHistory = useRef<(redo: boolean) => void>(() => {});
  useEffect(() => {
    stepHistory.current = stepAmountHistory;
  });
  useEffect(() => {
    const input = amountInput.current;
    if (!input) return;
    const isHistory = (event: InputEvent) =>
      event.inputType === "historyUndo" || event.inputType === "historyRedo";
    const onBeforeInput = (event: InputEvent) => {
      collapsedBeforeInput.current =
        input.selectionStart === input.selectionEnd;
      if (!isHistory(event)) return;
      event.preventDefault();
      stepHistory.current(event.inputType === "historyRedo");
    };
    const onInput = (event: Event) => {
      if (event instanceof InputEvent && isHistory(event))
        stepHistory.current(event.inputType === "historyRedo");
    };
    input.addEventListener("beforeinput", onBeforeInput);
    input.addEventListener("input", onInput);
    return () => {
      input.removeEventListener("beforeinput", onBeforeInput);
      input.removeEventListener("input", onInput);
    };
  }, []);
  const numericAmount = parseLoanAmount(amount);
  function updateInputs(
    nextAmount: string,
    nextMonths: string,
    nextRate: number,
    delay = 250,
  ) {
    setAmount(nextAmount);
    setMonths(nextMonths);
    setRate(nextRate);
    clearTimeout(pendingUpdate.current);
    const updateEstimate = () => {
      const parsed = calculatorSchema.safeParse({
        amount: parseLoanAmount(nextAmount),
        months: Number(nextMonths),
        rate: nextRate,
      });
      if (!parsed.success || !nextAmount.trim() || !nextMonths.trim()) {
        setError(
          "Enter an amount from $1 to $1,000,000 and a whole repayment period from 1 to 120 months. Showing the last valid estimate.",
        );
        return;
      }
      setError("");
      setResult({
        months: parsed.data.months,
        rate: parsed.data.rate,
        ...calculateLoan(parsed.data),
      });
    };
    if (delay === 0) updateEstimate();
    else pendingUpdate.current = setTimeout(updateEstimate, delay);
  }
  function recordAmount(entry: { amount: string; caret: number }) {
    const history = amountHistory.current;
    if (history.entries[history.index].amount === entry.amount) return;
    history.entries.splice(history.index + 1, Infinity, entry);
    if (history.entries.length > 100) history.entries.shift();
    history.index = history.entries.length - 1;
  }
  function showAmount(
    input: HTMLInputElement,
    next: { amount: string; caret: number },
  ) {
    input.value = `$${next.amount}`;
    if (document.activeElement === input)
      input.setSelectionRange(next.caret, next.caret);
    if (next.amount !== amount) updateInputs(next.amount, months, rate);
  }
  function editAmount(input: HTMLInputElement, inputType?: string) {
    const next = editLoanAmount(
      amount,
      input.value,
      input.selectionStart ?? input.value.length,
      inputType,
      collapsedBeforeInput.current,
    );
    collapsedBeforeInput.current = true;
    if (next.amount !== amount) {
      recordAmount({ amount, caret: amount.length + 1 });
      recordAmount(next);
    }
    showAmount(input, next);
  }
  function stepAmountHistory(redo: boolean) {
    const input = amountInput.current;
    if (!input) return;
    setComposingAmount(null);
    recordAmount({ amount, caret: amount.length + 1 });
    const history = amountHistory.current;
    const index = history.index + (redo ? 1 : -1);
    if (index < 0 || index >= history.entries.length) return;
    history.index = index;
    showAmount(input, history.entries[index]);
  }
  return (
    <div className="calculator" aria-labelledby="calculator-title">
      <form
        className="calculator-inputs"
        noValidate
        onSubmit={(e) => e.preventDefault()}
      >
        <h2 id="calculator-title">
          Wondering How Much Your Personal Loan Will Cost?
        </h2>
        <p className="calculator-intro">
          Get the numbers via our efficient personal loan calculator here.
        </p>
        <label htmlFor="loan-amount">
          Loan Amount (SGD) <span>*</span>
        </label>
        <input
          ref={amountInput}
          id="loan-amount"
          maxLength={20}
          inputMode="decimal"
          value={composingAmount ?? `$${amount}`}
          onChange={(e) => {
            const nativeEvent = e.nativeEvent as Partial<InputEvent>;
            if (nativeEvent.isComposing) {
              setComposingAmount(e.currentTarget.value);
              return;
            }
            setComposingAmount(null);
            if (
              nativeEvent.inputType !== "historyUndo" &&
              nativeEvent.inputType !== "historyRedo"
            )
              editAmount(e.currentTarget, nativeEvent.inputType);
          }}
          onCompositionEnd={(e) => {
            setComposingAmount(null);
            editAmount(e.currentTarget, "insertText");
          }}
          onKeyDown={(e) => {
            const key = e.key.toLowerCase();
            if (
              e.nativeEvent.isComposing ||
              !(e.metaKey || e.ctrlKey) ||
              e.altKey ||
              (key !== "z" && key !== "y")
            )
              return;
            e.preventDefault();
            stepAmountHistory(key === "y" || e.shiftKey);
          }}
          onBlur={() => setAmount(normalizeLoanAmount)}
          aria-describedby={error ? "calculator-error" : undefined}
          aria-invalid={Boolean(error)}
        />
        <input
          className="amount-slider"
          aria-label="Adjust loan amount"
          type="range"
          min="500"
          max="100000"
          step="1"
          style={
            {
              "--slider-fill": `${((Math.max(500, Math.min(100000, Number.isFinite(numericAmount) ? numericAmount : 25000)) - 500) / 99500) * 95 + 2.5}%`,
            } as CSSProperties
          }
          value={Math.max(
            500,
            Math.min(
              100000,
              Number.isFinite(numericAmount) ? numericAmount : 25000,
            ),
          )}
          onChange={(e) =>
            updateInputs(
              Number(e.target.value).toLocaleString("en-SG"),
              months,
              rate,
            )
          }
        />
        <div className="calculator-options">
          <div>
            <label htmlFor="loan-months">
              Repayment Period (Monthly) <span>*</span>
            </label>
            <input
              id="loan-months"
              type="number"
              min="1"
              max="120"
              step="1"
              value={months}
              onChange={(e) => updateInputs(amount, e.target.value, rate, 0)}
              aria-describedby={error ? "calculator-error" : undefined}
              aria-invalid={Boolean(error)}
            />
          </div>
          <div
            className="rate-group"
            role="group"
            aria-labelledby="loan-rate-label"
          >
            <p id="loan-rate-label" className="rate-label">
              Interest Rate % (Monthly) <span>*</span>
            </p>
            <div className="rate-buttons">
              {[1, 2, 3, 4].map((value) => (
                <button
                  key={value}
                  type="button"
                  aria-pressed={rate === value}
                  className={rate === value ? "active" : ""}
                  onClick={() => updateInputs(amount, months, value, 0)}
                >
                  {value}
                </button>
              ))}
            </div>
          </div>
        </div>
        {error && (
          <p id="calculator-error" className="form-error" role="alert">
            {error}
          </p>
        )}
      </form>
      <RepaymentSummary estimate={result} />
    </div>
  );
}
