import { useState } from 'react';
import { Sparkles, BrainCircuit, Minus, Plus, X } from 'lucide-react';
import type { Character, Defenses } from '@/lib/schemas';
import { castSpell, availableSlotLevels, dropConcentration } from '@/lib/mechanics';
import { ActionGuide } from './ActionGuide';
import { rollDice, applyRollMode } from '@/lib/dice/notation';
import { createRng } from '@/lib/rng';
import { useRollStore } from '@/stores/rollStore';
import { sendPlayerRoll } from '@/lib/sync/wsSync';
import { Button } from '@/components/ui/Button';
import { Input, Select } from '@/components/ui/Input';
import { Meter } from '@/components/ui/Codex';
import { cn } from '@/lib/cn';

const clamp = (n: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, n));

/** Interactive in-session sheet a seated player drives themselves. */
export function MyCharacterPanel({ character: c, onPatch }: { character: Character; onPatch: (diff: Partial<Character>) => void }) {
  const setHp = (current: number, temp = c.hp.temp) => onPatch({ hp: { current: clamp(current, 0, c.hp.max), max: c.hp.max, temp: Math.max(0, temp) } });
  const setSlot = (level: number, current: number) => onPatch({ spellcasting: { ...c.spellcasting, slots: c.spellcasting.slots.map((s) => (s.level === level ? { ...s, current } : s)) } });
  const setRes = (id: string, current: number) => onPatch({ resources: c.resources.map((r) => (r.id === id ? { ...r, current: clamp(current, 0, r.max) } : r)) });
  const setDef = (patch: Partial<Defenses>) => onPatch({ defenses: { ...c.defenses, ...patch } });

  const hpPct = c.hp.max > 0 ? clamp((c.hp.current / c.hp.max) * 100, 0, 100) : 0;
  const slots = c.spellcasting.slots.filter((s) => s.max > 0);
  const [castMsg, setCastMsg] = useState<string | null>(null);
  const [castLevel, setCastLevel] = useState<Record<string, number>>({});
  const doCast = (id: string, atLevel?: number) => {
    const r = castSpell(c, id, atLevel);
    if (r.ok) { onPatch(r.patch); setCastMsg(r.log.join(' ')); } else setCastMsg(r.reason);
  };

  return (
    <section data-testid="my-character" className="mb-6 rounded-xl border border-accent/40 bg-accent/5 p-4">
      <div className="mb-3 flex items-baseline justify-between">
        <h2 className="font-display text-2xl font-bold text-accent">{c.name}</h2>
        <span className="text-sm text-muted">Lv {c.level} {c.className} · {c.ancestry}</span>
      </div>

      {c.concentration && (
        <div className="mb-3 flex items-center gap-2 rounded-md border border-accent/50 bg-accent/10 px-3 py-1.5 text-sm">
          <BrainCircuit size={15} className="shrink-0 text-accent" aria-hidden />
          <span className="text-ink">Concentrating on <span className="font-medium">{c.concentration.spellName}</span></span>
          <Button size="sm" variant="ghost" className="ml-auto" onClick={() => onPatch(dropConcentration(c))}>Drop</Button>
        </div>
      )}

      <div className="grid gap-4 md:grid-cols-2">
        {/* HP */}
        <div className="rounded-lg border border-line bg-panel p-3">
          <div className="mb-1 flex items-center justify-between text-sm font-semibold text-ink">
            <span>Hit points</span>
            <span className="tabular-nums" data-testid="my-hp">{c.hp.current}/{c.hp.max}{c.hp.temp > 0 ? ` (+${c.hp.temp})` : ''}</span>
          </div>
          <div className="mb-2">
            <Meter value={c.hp.current} max={c.hp.max} tone={hpPct > 50 ? 'var(--app-verdigris)' : hpPct > 0 ? 'var(--app-accent)' : 'var(--app-danger)'} height={10} />
          </div>
          <div className="flex flex-wrap items-center gap-1">
            <Button size="sm" variant="danger" onClick={() => setHp(c.hp.current - 5)}>−5</Button>
            <Button size="sm" variant="secondary" onClick={() => setHp(c.hp.current - 1)}>−1</Button>
            <Button size="sm" variant="secondary" onClick={() => setHp(c.hp.current + 1)}>+1</Button>
            <Button size="sm" variant="primary" onClick={() => setHp(c.hp.current + 5)}>+5</Button>
            <span className="mx-1 h-4 w-px bg-line" />
            <span className="text-xs text-muted">Temp</span>
            <Button size="sm" variant="ghost" onClick={() => setHp(c.hp.current, c.hp.temp - 1)}><Minus size={14} aria-hidden /></Button>
            <Button size="sm" variant="ghost" onClick={() => setHp(c.hp.current, c.hp.temp + 1)}><Plus size={14} aria-hidden /></Button>
          </div>
        </div>

        {/* Defenses */}
        <div className="rounded-lg border border-line bg-panel p-3">
          <div className="mb-2 text-sm font-semibold text-ink">Defenses</div>
          {c.system === 'pf2e' ? (
            <div className="grid grid-cols-2 gap-2 text-sm">
              <Counter label="Dying" value={c.defenses.dying} min={0} max={4} onChange={(dying) => setDef({ dying })} />
              <Counter label="Wounded" value={c.defenses.wounded} min={0} max={6} onChange={(wounded) => setDef({ wounded })} />
              <Counter label="Doomed" value={c.defenses.doomed} min={0} max={4} onChange={(doomed) => setDef({ doomed })} />
              <Counter label="Hero pts" value={c.defenses.heroPoints} min={0} max={9} onChange={(heroPoints) => setDef({ heroPoints })} />
            </div>
          ) : (
            <div className="space-y-2 text-sm">
              <Pips label="Death saves" count={3} filled={c.defenses.deathSaves.successes} color="bg-success" onSet={(n) => setDef({ deathSaves: { ...c.defenses.deathSaves, successes: n } })} />
              <Pips label="Death saves (fails)" count={3} filled={c.defenses.deathSaves.failures} color="bg-danger" onSet={(n) => setDef({ deathSaves: { ...c.defenses.deathSaves, failures: n } })} />
              <div className="flex items-center justify-between">
                <Counter label="Exhaustion" value={c.defenses.exhaustion} min={0} max={6} onChange={(exhaustion) => setDef({ exhaustion })} />
                <label className="flex items-center gap-1"><input type="checkbox" checked={c.defenses.inspiration} onChange={(e) => setDef({ inspiration: e.target.checked })} /> Inspiration</label>
              </div>
            </div>
          )}
        </div>

        {/* Spell slots */}
        {slots.length > 0 && (
          <div className="rounded-lg border border-line bg-panel p-3">
            <div className="mb-2 text-sm font-semibold text-ink">Spell slots</div>
            <div className="space-y-1.5">
              {slots.map((s) => (
                <div key={s.level} className="flex items-center gap-2">
                  <span className="w-14 text-xs text-muted">Lvl {s.level}</span>
                  <div className="flex flex-wrap gap-1">
                    {Array.from({ length: s.max }, (_, i) => (
                      <button key={i} aria-label={`Level ${s.level} slot ${i + 1}`}
                        onClick={() => setSlot(s.level, i < s.current ? i : i + 1)}
                        className={cn('h-5 w-5 rounded-full border', i < s.current ? 'border-accent bg-accent' : 'border-line bg-surface')} />
                    ))}
                  </div>
                  <span className="ml-auto text-xs tabular-nums text-muted">{s.current}/{s.max}</span>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Spells — cast enforces slot consumption + concentration */}
        {c.spellcasting.spells.length > 0 && (
          <div className="rounded-lg border border-line bg-panel p-3">
            <div className="mb-2 text-sm font-semibold text-ink">Spells</div>
            <div className="max-h-44 space-y-1 overflow-y-auto">
              {[...c.spellcasting.spells].sort((a, b) => a.level - b.level || a.name.localeCompare(b.name)).map((s) => (
                <div key={s.id} className="flex items-center gap-2 text-sm">
                  <span className="w-12 shrink-0 text-xs text-muted">{s.level === 0 ? 'Cant' : `Lv ${s.level}`}</span>
                  <span className="flex-1 truncate text-ink">
                    {s.name}
                    {s.concentration && <BrainCircuit size={11} className="ml-1 inline text-accent" aria-label="Concentration" />}
                  </span>
                  {s.level === 0 ? (
                    <Button size="sm" variant="ghost" onClick={() => doCast(s.id)} aria-label={`Cast ${s.name}`}>
                      <Sparkles size={12} aria-hidden /> Cast
                    </Button>
                  ) : (() => {
                    const levels = availableSlotLevels(c.spellcasting, s.level);
                    if (levels.length === 0) return <span className="text-xs text-muted">no slots</span>;
                    const chosen = castLevel[s.id] && levels.includes(castLevel[s.id]!) ? castLevel[s.id]! : levels[0]!;
                    return (
                      <div className="flex items-center gap-1">
                        {levels.length > 1 && (
                          <Select className="w-auto py-0.5 text-xs" value={chosen} aria-label={`Slot level for ${s.name}`}
                            onChange={(e) => setCastLevel((p) => ({ ...p, [s.id]: Number(e.target.value) }))}>
                            {levels.map((l) => <option key={l} value={l}>{`L${l}`}</option>)}
                          </Select>
                        )}
                        <Button size="sm" variant="ghost" onClick={() => doCast(s.id, chosen)} aria-label={`Cast ${s.name} at level ${chosen}`}>
                          <Sparkles size={12} aria-hidden /> Cast{chosen !== s.level ? ` L${chosen}` : ''}
                        </Button>
                      </div>
                    );
                  })()}
                </div>
              ))}
            </div>
            {castMsg && <p className="mt-2 text-xs text-muted" aria-live="polite">{castMsg}</p>}
          </div>
        )}

        {/* Resources */}
        {c.resources.length > 0 && (
          <div className="rounded-lg border border-line bg-panel p-3">
            <div className="mb-2 text-sm font-semibold text-ink">Resources</div>
            <div className="space-y-1.5">
              {c.resources.map((r) => (
                <div key={r.id} className="flex items-center gap-2 text-sm">
                  <span className="flex-1 truncate text-ink">{r.name}</span>
                  <Counter label="" value={r.current} min={0} max={r.max} onChange={(n) => setRes(r.id, n)} suffix={`/${r.max}`} />
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Conditions */}
        <ConditionsBox character={c} onPatch={onPatch} />

        {/* Dice */}
        <DiceBox characterId={c.id} />
      </div>
      <ActionGuide character={c} />
    </section>
  );
}

function Counter({ label, value, min, max, onChange, suffix }: { label: string; value: number; min: number; max: number; onChange: (n: number) => void; suffix?: string }) {
  return (
    <div className="flex items-center gap-1">
      {label && <span className="flex-1 text-xs text-muted">{label}</span>}
      <Button size="sm" variant="ghost" onClick={() => onChange(clamp(value - 1, min, max))}><Minus size={14} aria-hidden /></Button>
      <span className="w-8 text-center tabular-nums text-ink">{value}{suffix ?? ''}</span>
      <Button size="sm" variant="ghost" onClick={() => onChange(clamp(value + 1, min, max))}><Plus size={14} aria-hidden /></Button>
    </div>
  );
}

function Pips({ label, count, filled, color, onSet }: { label: string; count: number; filled: number; color: string; onSet: (n: number) => void }) {
  return (
    <div className="flex items-center gap-2">
      <span className="w-24 text-xs text-muted">{label}</span>
      <div className="flex gap-1">
        {Array.from({ length: count }, (_, i) => (
          <button key={i} aria-label={`${label} ${i + 1}`} onClick={() => onSet(i < filled ? i : i + 1)}
            className={cn('h-5 w-5 rounded-full border', i < filled ? `border-transparent ${color}` : 'border-line bg-surface')} />
        ))}
      </div>
    </div>
  );
}

function ConditionsBox({ character: c, onPatch }: { character: Character; onPatch: (diff: Partial<Character>) => void }) {
  const [name, setName] = useState('');
  const add = () => {
    const n = name.trim();
    if (!n) return;
    onPatch({ conditions: [...c.conditions, { name: n }] });
    setName('');
  };
  const remove = (i: number) => onPatch({ conditions: c.conditions.filter((_, idx) => idx !== i) });
  return (
    <div className="rounded-lg border border-line bg-panel p-3">
      <div className="mb-2 text-sm font-semibold text-ink">Conditions</div>
      <div className="mb-2 flex flex-wrap gap-1">
        {c.conditions.length === 0 && <span className="text-xs text-muted">None.</span>}
        {c.conditions.map((cond, i) => (
          <button key={i} onClick={() => remove(i)} className="inline-flex items-center gap-1 rounded-full bg-warning/15 px-2 py-0.5 text-xs text-warning hover:bg-danger/20 hover:text-danger" title="Remove">
            {cond.name}{cond.value ? ` ${cond.value}` : ''} <X size={11} aria-hidden />
          </button>
        ))}
      </div>
      <div className="flex gap-1">
        <Input className="h-8" value={name} onChange={(e) => setName(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') add(); }} placeholder="Add condition…" aria-label="Condition name" />
        <Button size="sm" variant="secondary" onClick={add}>Add</Button>
      </div>
    </div>
  );
}

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

function DiceBox({ characterId }: { characterId: string }) {
  const [expr, setExpr] = useState('');
  const roll = (expression: string, label: string) => {
    const mode = useRollStore.getState().mode;
    const applied = applyRollMode(expression, mode);
    const result = rollDice(applied, createRng());
    const tag = mode === 'advantage' ? ' (adv)' : mode === 'disadvantage' ? ' (dis)' : '';
    useRollStore.getState().push({ label: label + tag, result });
    sendPlayerRoll(characterId, label + tag, applied, result.total, result.breakdown);
  };
  return (
    <div className="rounded-lg border border-line bg-panel p-3">
      <div className="mb-2 text-sm font-semibold text-ink">Dice</div>
      <div className="mb-2 flex flex-wrap gap-1">
        {QUICK_DICE.map((d) => <Button key={d} size="sm" variant="secondary" onClick={() => roll(d, d)}>{d}</Button>)}
      </div>
      <div className="flex gap-1">
        <Input className="h-8" value={expr} onChange={(e) => setExpr(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && expr.trim()) { roll(expr.trim(), expr.trim()); setExpr(''); } }} placeholder="e.g. 2d6+3" aria-label="Dice expression" />
        <Button size="sm" variant="primary" disabled={!expr.trim()} onClick={() => { roll(expr.trim(), expr.trim()); setExpr(''); }}>Roll</Button>
      </div>
    </div>
  );
}
