import { useEffect, useState } from 'react';
import { useLiveQuery } from 'dexie-react-hooks';
import { rollDice, applyRollMode, DiceParseError, type RollResult } from '@/lib/dice/notation';
import { createRng } from '@/lib/rng';
import { diceRepo } from '@/lib/db/repositories';
import { useUiStore } from '@/stores/uiStore';
import { useMacroStore } from '@/stores/macroStore';
import { useRollStore } from '@/stores/rollStore';
import { Page, PageHeader } from '@/components/ui/Page';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/cn';

const DICE = ['d4', 'd6', 'd8', 'd10', 'd12', 'd20', 'd100'];

export function DicePage() {
  const campaignId = useUiStore((s) => s.activeCampaignId) ?? undefined;
  const [expr, setExpr] = useState('1d20');
  const [last, setLast] = useState<RollResult | null>(null);
  const [rollCount, setRollCount] = useState(0);
  const [error, setError] = useState<string | null>(null);

  const history = useLiveQuery(() => diceRepo.recent(campaignId, 30), [campaignId], []);

  const macroKey = campaignId ?? '';
  const macros = useMacroStore((s) => s.byCampaign[macroKey]) ?? [];
  const addMacro = useMacroStore((s) => s.add);
  const removeMacro = useMacroStore((s) => s.remove);

  const mode = useRollStore((s) => s.mode);
  const toggleMode = useRollStore((s) => s.toggleMode);

  const roll = async (expression: string, label = '') => {
    try {
      const finalExpr = applyRollMode(expression, mode);
      const result = rollDice(finalExpr, createRng());
      setLast(result);
      setRollCount((n) => n + 1);
      setError(null);
      const modeTag = mode === 'advantage' ? ' (adv)' : mode === 'disadvantage' ? ' (dis)' : '';
      await diceRepo.add({
        ...(campaignId ? { campaignId } : {}),
        label: label + modeTag,
        expression: finalExpr,
        total: result.total,
        breakdown: result.breakdown,
      });
    } catch (e) {
      setError(e instanceof DiceParseError ? e.message : 'Could not roll that expression');
    }
  };

  return (
    <Page>
      <PageHeader
        title="Dice"
        subtitle="Standard notation: 2d6+3, 4d6kh3 (keep highest 3), 1d20."
        actions={
          history.length > 0 ? (
            <Button variant="ghost" onClick={() => void diceRepo.clear(campaignId)}>
              Clear history
            </Button>
          ) : null
        }
      />

      <div className="grid gap-6 lg:grid-cols-[1fr_320px]">
        <div>
          <div className="flex gap-2">
            <Input
              value={expr}
              onChange={(e) => setExpr(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === 'Enter') void roll(expr);
              }}
              placeholder="e.g. 2d6+3"
              aria-label="Dice expression"
            />
            <Button variant="primary" onClick={() => void roll(expr)}>
              Roll
            </Button>
          </div>

          <div className="mt-3 flex flex-wrap gap-2">
            {DICE.map((d) => (
              <Button key={d} size="sm" onClick={() => void roll(`1${d}`)}>
                {d}
              </Button>
            ))}
            <Button
              size="sm"
              variant={mode === 'advantage' ? 'primary' : 'secondary'}
              aria-pressed={mode === 'advantage'}
              onClick={() => toggleMode('advantage')}
              title="Toggle advantage — rolls the next die twice, keep highest"
            >
              Advantage
            </Button>
            <Button
              size="sm"
              variant={mode === 'disadvantage' ? 'primary' : 'secondary'}
              aria-pressed={mode === 'disadvantage'}
              onClick={() => toggleMode('disadvantage')}
              title="Toggle disadvantage — rolls the next die twice, keep lowest"
            >
              Disadvantage
            </Button>
          </div>
          {mode !== 'normal' && (
            <p className="mt-2 text-xs text-accent">
              {mode === 'advantage' ? 'Advantage' : 'Disadvantage'} is on — the next single die rolls twice, keeping the {mode === 'advantage' ? 'highest' : 'lowest'}.
            </p>
          )}

          {/* Saved rolls (macros) */}
          <div className="mt-4">
            <div className="flex items-center gap-2">
              <span className="text-xs font-semibold uppercase tracking-wide text-muted">Saved rolls</span>
              <Button
                size="sm"
                variant="ghost"
                disabled={expr.trim() === ''}
                onClick={() => addMacro(macroKey, expr.trim(), expr.trim())}
              >
                + Save “{expr.trim() || '…'}”
              </Button>
            </div>
            {macros.length > 0 && (
              <div className="mt-1 flex flex-wrap gap-1">
                {macros.map((m) => (
                  <span key={m.id} className="inline-flex items-center overflow-hidden rounded-md border border-line bg-panel">
                    <button
                      className="px-2 py-1 text-sm text-ink hover:bg-elevated"
                      onClick={() => { setExpr(m.expression); void roll(m.expression, m.label !== m.expression ? m.label : ''); }}
                    >
                      {m.label}
                    </button>
                    <button
                      className="px-1.5 text-muted hover:text-danger"
                      onClick={() => removeMacro(macroKey, m.id)}
                      aria-label={`Remove ${m.label}`}
                    >
                      ✕
                    </button>
                  </span>
                ))}
              </div>
            )}
          </div>

          {error && <p className="mt-4 text-sm text-danger">{error}</p>}

          {last && (
            <div className="mt-6 rounded-lg border border-line bg-panel p-6 text-center">
              <AnimatedTotal key={rollCount} result={last} />
              <div className="mt-2 font-mono text-sm text-muted">{last.breakdown}</div>
              <span className="sr-only" aria-live="polite">
                Rolled {last.expression}: {last.total}
              </span>
            </div>
          )}
        </div>

        <aside>
          <h2 className="mb-2 text-xs font-semibold uppercase tracking-wide text-muted">History</h2>
          {history.length === 0 ? (
            <p className="text-sm text-muted">No rolls yet.</p>
          ) : (
            <ul className="space-y-1.5" aria-live="polite">
              {history.map((h) => (
                <li
                  key={h.id}
                  className="flex items-center justify-between rounded-md border border-line bg-panel px-3 py-1.5 text-sm"
                >
                  <span className="text-muted">
                    {h.label ? `${h.label}: ` : ''}
                    <span className="font-mono">{h.expression}</span>
                  </span>
                  <span className="font-display text-lg font-semibold text-ink">{h.total}</span>
                </li>
              ))}
            </ul>
          )}
        </aside>
      </div>
    </Page>
  );
}

/**
 * Shows the rolling die "tumbling" through random numbers before settling on the
 * real total. Mounted with a per-roll key so each roll replays the animation.
 */
function AnimatedTotal({ result }: { result: RollResult }) {
  const [display, setDisplay] = useState(result.total);
  const [rolling, setRolling] = useState(true);

  useEffect(() => {
    setRolling(true);
    let frames = 0;
    const maxFrames = 9;
    const span = Math.max(6, Math.abs(result.total) + 4);
    const id = setInterval(() => {
      frames++;
      if (frames >= maxFrames) {
        clearInterval(id);
        setDisplay(result.total);
        setRolling(false);
      } else {
        setDisplay(Math.floor(Math.random() * span) + 1);
      }
    }, 45);
    return () => clearInterval(id);
  }, [result]);

  return (
    <div
      aria-hidden
      className={cn(
        'font-display text-6xl font-bold text-accent tabular-nums',
        rolling ? 'animate-dice-rolling opacity-80' : 'animate-dice-settle',
      )}
    >
      {display}
    </div>
  );
}
