import { useEffect, useState, useCallback } from "react";
import { usePersistentState } from "../lib/usePersistentState";
import { useToast } from "./Toast";

interface DieResult {
  notation: string;
  rolls: number[];
  total: number;
  modifier: number;
  advantage?: "adv" | "dis" | null;
  keptRoll?: number;
}

const PRESETS = ["d4", "d6", "d8", "d10", "d12", "d20", "d100"];

// ponytail: roll templates for the 4 things a DM rolls every combat.
// Each template uses the modifier typed in the input box, so the DM
// sets "+5" once and templates respect it. 5e convention, not house rule.
const TEMPLATES: { label: string; die: string }[] = [
  { label: "Attack", die: "d20" },
  { label: "Save", die: "d20" },
  { label: "Check", die: "d20" },
  { label: "Damage", die: "d8" },
];

type Mode = "normal" | "adv" | "dis";

function rollDie(sides: number): number {
  return Math.floor(Math.random() * sides) + 1;
}

function parseNotation(
  notation: string,
): { count: number; sides: number; modifier: number } | null {
  const match = notation.trim().toLowerCase().match(/^(\d+)?d(\d+)([+-]\d+)?$/);
  if (!match) return null;
  return {
    count: parseInt(match[1] || "1"),
    sides: parseInt(match[2]),
    modifier: parseInt(match[3] || "0"),
  };
}

// ponytail: advantage/disadvantage is a 5e concept. We implement it as
// "roll twice, keep the (higher|lower) d20", which matches the PHB. Only
// meaningful for d20 rolls; for other dice we just roll normally.
function applyMode(rolls: number[], sides: number, mode: Mode): { rolls: number[]; kept: number } {
  if (mode === "normal" || sides !== 20 || rolls.length !== 2) {
    return { rolls, kept: rolls[0] ?? 0 };
  }
  if (mode === "adv") {
    const hi = Math.max(rolls[0], rolls[1]);
    return { rolls, kept: hi };
  }
  const lo = Math.min(rolls[0], rolls[1]);
  return { rolls, kept: lo };
}

export function DiceRoller() {
  const [input, setInput] = usePersistentState<string>("dice.input", "1d20");
  const [results, setResults] = usePersistentState<DieResult[]>("dice.history", []);
  const [lastTotal, setLastTotal] = useState<number | null>(null);
  const [lastBreakdown, setLastBreakdown] = useState<DieResult | null>(null);
  const [mode, setMode] = usePersistentState<Mode>("dice.mode", "normal");
  const { addToast } = useToast();

  const roll = useCallback(
    (overrideNotation?: string) => {
      const notation = (overrideNotation ?? input).trim();
      const parsed = parseNotation(notation);
      if (!parsed || parsed.sides < 2 || parsed.count > 100) return;

      // For d20 + adv/dis, roll an extra die to compare.
      const baseCount = parsed.count;
      const isD20AdvDis = parsed.sides === 20 && baseCount === 1 && mode !== "normal";
      const rollCount = isD20AdvDis ? 2 : baseCount;
      const rawRolls = Array.from({ length: rollCount }, () => rollDie(parsed.sides));
      const { rolls, kept } = applyMode(rawRolls, parsed.sides, mode);
      const sum = kept + parsed.modifier;

      const result: DieResult = {
        notation: mode !== "normal" && parsed.sides === 20
          ? `${notation} (${mode === "adv" ? "adv" : "dis"})`
          : notation,
        rolls,
        total: sum,
        modifier: parsed.modifier,
        advantage: mode !== "normal" && parsed.sides === 20 ? mode : null,
        keptRoll: mode !== "normal" && parsed.sides === 20 ? kept : undefined,
      };

      setResults((prev) => [result, ...prev].slice(0, 50));
      setLastTotal(sum);
      setLastBreakdown(result);
    },
    [input, mode],
  );

  // ponytail: spacebar rolls the current notation when no input is focused.
  // Hands-free dice at the table is the whole reason a DM uses a digital roller.
  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      if (e.code !== "Space") return;
      if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
      e.preventDefault();
      roll();
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [roll]);

  function clearHistory() {
    setResults([]);
    setLastTotal(null);
    setLastBreakdown(null);
  }

  function copyHistory() {
    if (results.length === 0) return;
    const lines = results
      .slice()
      .reverse()
      .map((r) => {
        const detail = r.rolls.length > 1 ? ` [${r.rolls.join(", ")}]` : "";
        return `${r.notation}${detail} → ${r.total}`;
      });
    void navigator.clipboard.writeText(lines.join("\n")).then(
      () => addToast("History copied", "success"),
      () => addToast("Copy failed", "error"),
    );
  }

  // ponytail: apply a template (Attack/Save/Check/Damage). The current input's
  // trailing modifier is reused so "+5" typed once works for all 5e rolls.
  // The template label is recorded in the most recent history row so the
  // DM can scan "Attack 1d20+5 → 17" instead of a wall of bare notation.
  function applyTemplate(template: { notation: string; label: string }) {
    roll(template.notation);
    setResults((prev) => {
      if (prev.length === 0) return prev;
      const [head, ...rest] = prev;
      return [
        { ...head, notation: `${template.label} (${head.notation})` },
        ...rest,
      ];
    });
  }

  return (
    <div className="flex flex-col gap-3 h-full">
      {/* Big result display */}
      <div className="flex flex-col items-center justify-center py-3 gap-1">
        <span className="font-mono text-4xl font-bold text-[var(--color-gold-bright)]">
          {lastTotal !== null ? lastTotal : "—"}
        </span>
        {lastBreakdown && (
          <span className="font-mono text-[11px] text-[var(--color-text-secondary)]">
            {formatBreakdown(lastBreakdown)}
          </span>
        )}
      </div>

      {/* Input row */}
      <div className="flex gap-2">
        <input
          className="flex-1 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-3 py-2 text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none focus:border-[var(--color-gold-bright)] text-sm font-mono"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && roll()}
          placeholder="2d6+3"
        />
        <button
          onClick={() => roll()}
          className="rounded-lg bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)] px-4 py-2 text-sm font-semibold hover:bg-[var(--color-gold-muted)] transition-colors cursor-pointer"
          title="Roll (Space)"
        >
          Roll
        </button>
      </div>

      {/* Mode toggle — advantage / disadvantage / normal */}
      <div className="flex items-center gap-1.5">
        <span className="text-[var(--color-text-dim)] text-xs mr-1">d20 mode:</span>
        {(["normal", "adv", "dis"] as const).map((m) => (
          <button
            key={m}
            onClick={() => setMode(m)}
            className={`rounded px-2.5 py-1 text-xs cursor-pointer transition-colors ${
              mode === m
                ? "bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)] font-semibold"
                : "bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-secondary)] hover:border-[var(--color-gold-bright)]"
            }`}
            title={m === "adv" ? "Roll twice, keep higher" : m === "dis" ? "Roll twice, keep lower" : "Normal"}
          >
            {m === "normal" ? "Normal" : m === "adv" ? "Adv" : "Dis"}
          </button>
        ))}
        <span className="text-[var(--color-text-dim)] text-[10px] ml-auto" aria-hidden="true">
          space to roll
        </span>
      </div>

      {/* Quick dice */}
      <div className="flex flex-wrap gap-1.5">
        {PRESETS.map((d) => (
          <button
            key={d}
            onClick={() => roll(`1${d}`)}
            className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-3 py-1 text-xs text-[var(--color-text-secondary)] hover:border-[var(--color-gold-bright)] hover:text-[var(--color-gold-bright)] transition-colors cursor-pointer font-mono"
          >
            {d}
          </button>
        ))}
      </div>

      {/* Roll templates — the 4 things a DM rolls most. */}
      <div className="flex flex-wrap gap-1.5">
        <span className="text-[var(--color-text-dim)] text-[10px] uppercase tracking-wider w-full">
          Templates (use current modifier)
        </span>
        {TEMPLATES.map((t) => {
          const modMatch = input.match(/[+-]\s*\d+/);
          const mod = modMatch ? modMatch[0].replace(/\s+/g, "") : "+0";
          const notation = `1${t.die}${mod}`;
          return (
            <button
              key={t.label}
              onClick={() => applyTemplate({ label: t.label, notation })}
              className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-2.5 py-1 text-xs text-[var(--color-text-secondary)] hover:border-[var(--color-gold-bright)] hover:text-[var(--color-gold-bright)] transition-colors cursor-pointer"
              title={`${t.label} — ${notation}`}
            >
              {t.label}
            </button>
          );
        })}
      </div>

      {/* History */}
      <div className="flex-1 overflow-y-auto">
        <div className="flex items-center justify-between mb-1">
          <span className="text-[10px] uppercase tracking-wider text-[var(--color-text-dim)]">
            History
          </span>
          {results.length > 0 && (
            <div className="flex items-center gap-2">
              <button
                onClick={copyHistory}
                className="text-[10px] text-[var(--color-text-dim)] hover:text-[var(--color-gold-bright)] cursor-pointer transition-colors"
              >
                copy
              </button>
              <button
                onClick={clearHistory}
                className="text-[10px] text-[var(--color-text-dim)] hover:text-[var(--color-danger)] cursor-pointer transition-colors"
              >
                clear
              </button>
            </div>
          )}
        </div>
        <div className="flex flex-col gap-1">
          {results.map((r, i) => (
            <div
              key={i}
              className="flex items-center justify-between rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-subtle)] px-3 py-1.5"
            >
              <span className="font-mono text-xs text-[var(--color-text-secondary)] truncate">
                {r.notation}
                {r.rolls.length > 1 && ` [${r.rolls.join(", ")}]`}
              </span>
              <span className="font-mono text-sm font-bold text-[var(--color-gold-bright)] shrink-0 ml-2">
                {r.total}
              </span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function formatBreakdown(r: DieResult): string {
  if (r.advantage && r.keptRoll !== undefined) {
    return `[${r.rolls.join(", ")}] keep ${r.advantage === "adv" ? "hi" : "lo"}${r.modifier !== 0 ? ` ${r.modifier > 0 ? "+" : ""}${r.modifier}` : ""} = ${r.total}`;
  }
  if (r.rolls.length > 1 || r.modifier !== 0) {
    const base = r.rolls.length > 1 ? `[${r.rolls.join("+")}]` : `${r.rolls[0]}`;
    const mod = r.modifier !== 0 ? ` ${r.modifier > 0 ? "+" : ""}${r.modifier}` : "";
    return `${base}${mod} = ${r.total}`;
  }
  return `${r.rolls[0]}`;
}
