"use client";

import { useState, useCallback } from "react";
import { Card } from "@/components/ui/card";
import { StepControls } from "./step-controls";
import { parseStrictDecimal } from "@/lib/math/validation";

type RoundTarget = "whole" | "1dp" | "2dp" | "1sf" | "2sf" | "3sf";

interface Step {
  label: string;
  digits: DigitInfo[];
  resultText?: string;
}

interface DigitInfo {
  char: string;
  state: "normal" | "target" | "decider" | "dim" | "significant";
}

function getDigits(value: string): string[] {
  return value.split("");
}

// ── Decimal place rounding steps ─────────────────────────────────────────────
function buildDPSteps(rawValue: string, target: "whole" | "1dp" | "2dp"): Step[] {
  const value = parseFloat(rawValue);
  const digits = getDigits(rawValue);
  const dotIndex = digits.indexOf(".");
  const steps: Step[] = [];

  function makeDigits(
    targetIdx: number | null,
    deciderIdx: number | null,
  ): DigitInfo[] {
    return digits.map((ch, i) => ({
      char: ch,
      state:
        ch === "."
          ? "normal"
          : i === targetIdx
            ? "target"
            : i === deciderIdx
              ? "decider"
              : "normal",
    }));
  }

  let targetDigitIdx: number;
  let roundedValue: number;
  let targetLabel: string;

  if (target === "whole") {
    targetDigitIdx = dotIndex === -1 ? digits.length - 1 : dotIndex - 1;
    roundedValue = Math.round(value);
    targetLabel = "ones (whole number)";
  } else if (target === "1dp") {
    targetDigitIdx = dotIndex + 1;
    roundedValue = Math.round(value * 10) / 10;
    targetLabel = "1 decimal place (tenths)";
  } else {
    targetDigitIdx = dotIndex + 2;
    roundedValue = Math.round(value * 100) / 100;
    targetLabel = "2 decimal places (hundredths)";
  }

  // Compute decider index (skip over decimal point if needed)
  const nextIdx = targetDigitIdx + 1;
  const deciderIdx =
    nextIdx < digits.length && digits[nextIdx] === "."
      ? nextIdx + 1
      : nextIdx;

  // Step 0: Show the number
  steps.push({
    label: `Rounding ${rawValue} to ${targetLabel}`,
    digits: makeDigits(null, null),
  });

  // Step 1: Identify target
  steps.push({
    label: `Identify the target digit (${targetLabel})`,
    digits: makeDigits(targetDigitIdx, null),
  });

  // Step 2: Identify decider
  if (deciderIdx < digits.length) {
    steps.push({
      label: "Look at the digit to the right (the decider)",
      digits: makeDigits(targetDigitIdx, deciderIdx),
    });

    const deciderDigit = parseInt(digits[deciderIdx]);
    const roundsUp = deciderDigit >= 5;
    steps.push({
      label: roundsUp
        ? `Decider is ${deciderDigit} (≥ 5) → round UP`
        : `Decider is ${deciderDigit} (< 5) → round DOWN`,
      digits: makeDigits(targetDigitIdx, deciderIdx),
    });
  } else {
    steps.push({
      label: "No digit to the right — no rounding needed",
      digits: makeDigits(targetDigitIdx, null),
    });
  }

  // Step 3: Result
  const resultStr =
    target === "whole"
      ? roundedValue.toString()
      : target === "1dp"
        ? roundedValue.toFixed(1)
        : roundedValue.toFixed(2);

  const resultDigits = getDigits(resultStr);
  steps.push({
    label: `Result: ${resultStr}`,
    digits: resultDigits.map((ch) => ({
      char: ch,
      state: "normal" as const,
    })),
    resultText: resultStr,
  });

  return steps;
}

// ── Significant figures rounding steps ───────────────────────────────────────
function buildSFSteps(rawValue: string, sfCount: number): Step[] {
  const value = parseFloat(rawValue);
  const digits = getDigits(rawValue);
  const steps: Step[] = [];

  // Step 0: Show the number
  steps.push({
    label: `Rounding ${rawValue} to ${sfCount} significant figure${sfCount > 1 ? "s" : ""}`,
    digits: digits.map((ch) => ({ char: ch, state: "normal" as const })),
  });

  // Step 1: Identify which digits are significant
  // Rule: significant figures start at the first non-zero digit
  const sigDigitIndices: number[] = [];
  let foundFirstNonZero = false;
  for (let i = 0; i < digits.length; i++) {
    if (digits[i] === ".") continue;
    if (!foundFirstNonZero && digits[i] === "0") continue;
    foundFirstNonZero = true;
    sigDigitIndices.push(i);
  }

  // Build display: highlight all significant digits found
  const sigSet = new Set(sigDigitIndices);
  steps.push({
    label: `Identify significant figures — start from the first non-zero digit`,
    digits: digits.map((ch, i) => ({
      char: ch,
      state:
        ch === "."
          ? "normal"
          : sigSet.has(i)
            ? "significant"
            : "dim",
    })),
  });

  // Count and label the sig figs we need
  const sigLabel = sigDigitIndices.slice(0, sfCount).map((idx, n) => {
    return `${ordinal(n + 1)} s.f. = ${digits[idx]}`;
  });
  const targetIdx = sigDigitIndices[sfCount - 1]; // the last sig fig we keep

  if (targetIdx === undefined) {
    // Not enough significant figures in the number
    steps.push({
      label: `The number only has ${sigDigitIndices.length} significant figure${sigDigitIndices.length !== 1 ? "s" : ""} — already rounded`,
      digits: digits.map((ch, i) => ({
        char: ch,
        state:
          ch === "."
            ? "normal"
            : sigSet.has(i)
              ? "significant"
              : "dim",
      })),
      resultText: rawValue,
    });
    return steps;
  }

  // Step 2: Show which sig figs we're keeping
  const keepSet = new Set(sigDigitIndices.slice(0, sfCount));
  steps.push({
    label: `Keep ${sfCount} sig fig${sfCount > 1 ? "s" : ""}: ${sigLabel.join(", ")}`,
    digits: digits.map((ch, i) => ({
      char: ch,
      state:
        ch === "."
          ? "normal"
          : keepSet.has(i)
            ? "target"
            : sigSet.has(i)
              ? "normal"
              : "dim",
    })),
  });

  // Step 3: Identify the decider
  const deciderCandidateIdx = sigDigitIndices[sfCount]; // next sig fig after the ones we keep
  // But the decider could also be after a decimal point
  let deciderIdx: number | null = null;
  if (deciderCandidateIdx !== undefined) {
    deciderIdx = deciderCandidateIdx;
  } else {
    // Look for any digit after the target
    for (let i = targetIdx + 1; i < digits.length; i++) {
      if (digits[i] !== ".") {
        deciderIdx = i;
        break;
      }
    }
  }

  if (deciderIdx !== null && deciderIdx < digits.length) {
    steps.push({
      label: `Look at the next digit: ${digits[deciderIdx]} (the decider)`,
      digits: digits.map((ch, i) => ({
        char: ch,
        state:
          ch === "."
            ? "normal"
            : i === deciderIdx
              ? "decider"
              : keepSet.has(i)
                ? "target"
                : "dim",
      })),
    });

    // Step 4: Round decision
    const deciderDigit = parseInt(digits[deciderIdx]);
    const roundsUp = deciderDigit >= 5;
    steps.push({
      label: roundsUp
        ? `Decider is ${deciderDigit} (≥ 5) → round UP the last kept digit`
        : `Decider is ${deciderDigit} (< 5) → round DOWN (keep it the same)`,
      digits: digits.map((ch, i) => ({
        char: ch,
        state:
          ch === "."
            ? "normal"
            : i === deciderIdx
              ? "decider"
              : keepSet.has(i)
                ? "target"
                : "dim",
      })),
    });
  } else {
    steps.push({
      label: "No digit to the right — no rounding needed",
      digits: digits.map((ch, i) => ({
        char: ch,
        state:
          ch === "."
            ? "normal"
            : keepSet.has(i)
              ? "target"
              : "dim",
      })),
    });
  }

  // Step 5: Compute and show result
  let roundedValue: number;
  if (value === 0) {
    roundedValue = 0;
  } else {
    const magnitude = Math.floor(Math.log10(Math.abs(value)));
    const factor = Math.pow(10, sfCount - 1 - magnitude);
    roundedValue = Math.round(value * factor) / factor;
  }

  // Format result with correct sig figs (preserve trailing zeros)
  const resultStr = formatSigFigs(roundedValue, sfCount);
  const resultDigits = getDigits(resultStr);

  // Step: explain what happens to remaining digits
  const dotIdx = rawValue.indexOf(".");
  const hasIntegerPart = dotIdx === -1 || dotIdx > 0;
  if (hasIntegerPart && sigDigitIndices.length > sfCount) {
    // Some trailing digits in the integer part get replaced with zeros
    steps.push({
      label: "Replace remaining digits with zeros (to keep the place value)",
      digits: resultDigits.map((ch) => ({
        char: ch,
        state: "normal" as const,
      })),
    });
  }

  steps.push({
    label: `Result: ${resultStr} (${sfCount} significant figure${sfCount > 1 ? "s" : ""})`,
    digits: resultDigits.map((ch) => ({
      char: ch,
      state: "normal" as const,
    })),
    resultText: resultStr,
  });

  return steps;
}

function formatSigFigs(value: number, sfCount: number): string {
  if (value === 0) return "0";

  const magnitude = Math.floor(Math.log10(Math.abs(value)));

  if (magnitude >= sfCount - 1) {
    // Integer result — no decimal point needed
    return Math.round(value / Math.pow(10, magnitude - sfCount + 1))
      * Math.pow(10, magnitude - sfCount + 1) + "";
  }

  // Decimal result — need to show enough places
  const decimalPlaces = sfCount - 1 - magnitude;
  return value.toFixed(decimalPlaces);
}

function ordinal(n: number): string {
  if (n === 1) return "1st";
  if (n === 2) return "2nd";
  if (n === 3) return "3rd";
  return `${n}th`;
}

// ── Main step builder ────────────────────────────────────────────────────────
function buildSteps(rawValue: string, target: RoundTarget): Step[] {
  if (target === "whole" || target === "1dp" || target === "2dp") {
    return buildDPSteps(rawValue, target);
  }
  const sfCount = target === "1sf" ? 1 : target === "2sf" ? 2 : 3;
  return buildSFSteps(rawValue, sfCount);
}

// ── Digit box component ─────────────────────────────────────────────────────
function DigitBox({ char, state }: DigitInfo) {
  if (char === ".") {
    return (
      <div className="flex h-[88px] w-6 items-end justify-center pb-2 max-sm:h-[70px]">
        <div className="h-3 w-3 rounded-full bg-foreground" />
      </div>
    );
  }

  const styles: Record<DigitInfo["state"], string> = {
    normal: "border-border bg-surface text-foreground",
    target: "border-unit-2 bg-unit-2-light text-foreground ring-2 ring-unit-2/30",
    decider: "border-hint bg-hint-light text-foreground ring-2 ring-hint/30",
    significant: "border-unit-4 bg-unit-4-light text-foreground ring-2 ring-unit-4/30",
    dim: "border-border/40 bg-background text-muted/30",
  };

  return (
    <div
      className={`flex h-[88px] w-[68px] shrink-0 select-none items-center justify-center rounded-xl border-2 text-[3.2rem] font-bold transition-all duration-300 max-sm:h-[70px] max-sm:w-[52px] max-sm:text-[2.4rem] ${styles[state]}`}
    >
      {char}
    </div>
  );
}

// ── Explorer component ──────────────────────────────────────────────────────
export function RoundingExplorer() {
  const [input, setInput] = useState("3.4567");
  const [target, setTarget] = useState<RoundTarget>("1dp");
  const [error, setError] = useState("");

  const [steps, setSteps] = useState<Step[] | null>(null);
  const [currentStep, setCurrentStep] = useState(0);
  const [isPlaying, setIsPlaying] = useState(false);

  const done = steps ? currentStep >= steps.length - 1 : false;

  function handleGo() {
    setError("");
    const trimmed = input.trim();
    const val = parseStrictDecimal(trimmed);
    if (val === null) {
      setError("Enter a valid number (digits and decimal point only).");
      return;
    }
    if (val < 0) {
      setError("Enter a positive number.");
      return;
    }
    if (trimmed.length > 16) {
      setError("Number too long.");
      return;
    }
    if (!trimmed.includes(".") && (target === "1dp" || target === "2dp")) {
      setError("Enter a decimal number for decimal place rounding.");
      return;
    }
    try {
      const s = buildSteps(trimmed, target);
      setSteps(s);
      setCurrentStep(0);
      setIsPlaying(false);
    } catch {
      setError("Could not process. Check your input.");
    }
  }

  const stepForward = useCallback(() => {
    if (!steps || currentStep >= steps.length - 1) return;
    setCurrentStep((s) => s + 1);
    if (currentStep + 1 >= steps.length - 1) setIsPlaying(false);
  }, [steps, currentStep]);

  const stepBack = useCallback(() => {
    if (currentStep <= 0) return;
    setCurrentStep((s) => s - 1);
  }, [currentStep]);

  const togglePlay = useCallback(() => setIsPlaying((p) => !p), []);

  const reset = useCallback(() => {
    setSteps(null);
    setCurrentStep(0);
    setIsPlaying(false);
  }, []);

  const step = steps ? steps[currentStep] : null;

  const targetLabels: Record<RoundTarget, string> = {
    whole: "Whole Number",
    "1dp": "1 d.p.",
    "2dp": "2 d.p.",
    "1sf": "1 s.f.",
    "2sf": "2 s.f.",
    "3sf": "3 s.f.",
  };

  return (
    <div className="space-y-4">
      {/* Target tabs */}
      <div className="flex flex-wrap gap-2">
        {(["whole", "1dp", "2dp", "1sf", "2sf", "3sf"] as RoundTarget[]).map((t) => (
          <button
            key={t}
            onClick={() => {
              setTarget(t);
              reset();
            }}
            className={`rounded-full border-2 px-4 py-2 text-sm font-semibold transition-colors ${
              target === t
                ? "border-unit-2 bg-unit-2 text-white"
                : "border-unit-2/40 text-unit-2 hover:bg-unit-2-light"
            }`}
          >
            {targetLabels[t]}
          </button>
        ))}
      </div>

      {/* Input */}
      <Card>
        <p className="mb-3 text-xs font-bold uppercase tracking-wider text-unit-2">
          Enter a number
        </p>
        <div className="flex flex-wrap items-center gap-2.5">
          <input
            type="text"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            onKeyDown={(e) => e.key === "Enter" && handleGo()}
            placeholder="e.g. 3.4567 or 0.00456"
            maxLength={18}
            autoComplete="off"
            className="max-w-[240px] rounded-lg border-2 border-border bg-surface px-3.5 py-2.5 text-lg font-medium outline-none transition-colors focus:border-unit-2"
          />
          <button
            onClick={handleGo}
            className="rounded-lg bg-unit-2 px-6 py-2.5 text-sm font-bold text-white transition-colors hover:bg-unit-2-dark"
          >
            Round →
          </button>
        </div>
        {error && <p className="mt-2 text-sm text-incorrect">{error}</p>}
      </Card>

      {/* Display */}
      <Card className="flex min-h-[220px] flex-col items-center justify-center gap-4 p-6">
        {!step ? (
          <p className="text-muted/50">
            Enter a number above and click <strong>Round</strong>
          </p>
        ) : (
          <>
            <p className="text-sm font-medium text-muted">{step.label}</p>
            <div className="flex flex-wrap items-end justify-center gap-2">
              {step.digits.map((d, i) => (
                <DigitBox key={i} {...d} />
              ))}
            </div>
            {/* Legend */}
            <div className="flex flex-wrap items-center justify-center gap-4 text-xs text-muted">
              <span className="flex items-center gap-1">
                <span className="inline-block h-3 w-3 rounded border-2 border-unit-2 bg-unit-2-light" />
                Target digit
              </span>
              <span className="flex items-center gap-1">
                <span className="inline-block h-3 w-3 rounded border-2 border-hint bg-hint-light" />
                Decider digit
              </span>
              <span className="flex items-center gap-1">
                <span className="inline-block h-3 w-3 rounded border-2 border-unit-4 bg-unit-4-light" />
                Significant
              </span>
              <span className="flex items-center gap-1">
                <span className="inline-block h-3 w-3 rounded border-2 border-border/40 bg-background" />
                Not significant
              </span>
            </div>
          </>
        )}
      </Card>

      {/* Controls */}
      <Card className="p-4">
        <StepControls
          currentStep={currentStep + 1}
          totalSteps={steps?.length ?? 0}
          isPlaying={isPlaying}
          onStepForward={stepForward}
          onStepBack={stepBack}
          onTogglePlay={togglePlay}
          onReset={reset}
          canStepForward={!!steps && !done}
          canStepBack={!!steps && currentStep > 0}
        />
      </Card>

      {/* Result */}
      <Card className="flex min-h-[80px] flex-col items-center justify-center gap-1.5 text-center">
        {!done || !step?.resultText ? (
          <p className="text-sm text-muted/40">Result will appear here when steps are complete</p>
        ) : (
          <>
            <p className="text-xs uppercase tracking-wider text-muted">Rounded Value</p>
            <p className="text-3xl font-extrabold text-foreground max-sm:text-2xl">
              {step.resultText}
            </p>
          </>
        )}
      </Card>
    </div>
  );
}
