import { useEffect, useMemo, useState } from 'react';
import { useNavigate } from '@tanstack/react-router';
import type { Campaign, Character } from '@/lib/schemas';
import { charactersRepo } from '@/lib/db/repositories';
import {
  getSystem, ABILITY_ABBR, abilityModifier, type AbilityKey, type AbilityScores,
  getClassNames, getClassDef, classSkillChoices, classSkillCount, buildCharacter,
} from '@/lib/rules';
import { STANDARD_ARRAY, rollAbilityScores, pointBuyRemaining, POINT_BUY_MIN, POINT_BUY_MAX } from '@/lib/rules/abilityGen';
import { loadPf2e } from '@/lib/compendium';
import { createRng } from '@/lib/rng';
import { formatModifier } from '@/lib/format';
import { Modal } from '@/components/ui/Modal';
import { Button } from '@/components/ui/Button';
import { Field, Input, Select } from '@/components/ui/Input';
import { NumberField } from '@/components/ui/NumberField';
import { cn } from '@/lib/cn';

const ABILITIES: AbilityKey[] = ['str', 'dex', 'con', 'int', 'wis', 'cha'];
const HIT_DICE = [6, 8, 10, 12];
type AbilityMethod = 'standard' | 'pointbuy' | 'roll';
type Ancestry = { name: string; hp?: number; speed?: number };

const STEPS = ['Basics', 'Abilities', 'Skills', 'Review'] as const;

export function CreationWizard({ campaign, onClose }: { campaign: Campaign; onClose: () => void }) {
  const navigate = useNavigate();
  const sys = getSystem(campaign.system);
  const allSkillKeys = sys.skills.map((s) => s.key);
  const skillLabel = (key: string) => sys.skills.find((s) => s.key === key)?.label ?? key;

  const [step, setStep] = useState(0);

  // Basics
  const [name, setName] = useState('');
  const [kind, setKind] = useState<Character['kind']>('pc');
  const [level, setLevel] = useState(1);
  const [classChoice, setClassChoice] = useState(getClassNames(campaign.system)[0] ?? 'Custom');
  const [customClass, setCustomClass] = useState('');
  const [hitDie, setHitDie] = useState(8);
  const [ancestry, setAncestry] = useState('');
  const [background, setBackground] = useState('');

  // PF2e ancestries (for HP/speed)
  const [ancestries, setAncestries] = useState<Ancestry[]>([]);
  useEffect(() => {
    if (campaign.system !== 'pf2e') return;
    let live = true;
    void loadPf2e('ancestries').then((rows) => {
      if (!live) return;
      setAncestries(rows.map((r) => {
        const speed = r.speed as { land?: number } | undefined;
        return {
          name: String(r.name),
          ...(typeof r.hp === 'number' ? { hp: r.hp } : {}),
          ...(speed?.land ? { speed: speed.land } : {}),
        };
      }));
    });
    return () => { live = false; };
  }, [campaign.system]);

  const className = classChoice === 'Custom' ? customClass.trim() : classChoice;
  const def = getClassDef(campaign.system, className);
  const selectedAncestry = ancestries.find((a) => a.name === ancestry);

  // Abilities
  const [method, setMethod] = useState<AbilityMethod>('standard');
  const [pool, setPool] = useState<number[]>([...STANDARD_ARRAY]);
  const [assignment, setAssignment] = useState<number[]>(ABILITIES.map((_, i) => i));
  const [pb, setPb] = useState<number[]>([8, 8, 8, 8, 8, 8]);
  const usesPool = method === 'standard' || method === 'roll';
  const chooseMethod = (m: AbilityMethod) => {
    setMethod(m);
    if (m === 'standard') { setPool([...STANDARD_ARRAY]); setAssignment(ABILITIES.map((_, i) => i)); }
    if (m === 'roll') { setPool(rollAbilityScores(createRng())); setAssignment(ABILITIES.map((_, i) => i)); }
  };
  const abilities = useMemo<AbilityScores>(() => {
    const out = {} as AbilityScores;
    ABILITIES.forEach((a, i) => { out[a] = usesPool ? (pool[assignment[i]!] ?? 10) : pb[i]!; });
    return out;
  }, [usesPool, pool, assignment, pb]);
  const poolValid = !usesPool || new Set(assignment).size === ABILITIES.length;
  const pbValid = method !== 'pointbuy' || pointBuyRemaining(pb) >= 0;

  // Skills
  const [skills, setSkills] = useState<string[]>([]);
  const intMod = abilityModifier(abilities.int);
  const skillCount = classSkillCount(campaign.system, className, intMod);
  const skillOptions = classSkillChoices(campaign.system, allSkillKeys, className);
  const toggleSkill = (key: string) =>
    setSkills((prev) => (prev.includes(key) ? prev.filter((k) => k !== key) : prev.length < skillCount ? [...prev, key] : prev));

  // Derived preview
  const built = useMemo(
    () => buildCharacter(campaign.system, {
      className, level, abilities, skillChoices: skills,
      ...(selectedAncestry?.hp ? { ancestryHp: selectedAncestry.hp } : {}),
      hitDieOverride: hitDie,
    }),
    [campaign.system, className, level, abilities, skills, selectedAncestry, hitDie],
  );

  const stepValid = [
    name.trim().length > 0 && className.length > 0,
    poolValid && pbValid,
    skills.length === Math.min(skillCount, skillOptions.length),
    true,
  ];

  const finish = async () => {
    const created = await charactersRepo.create(campaign.id, {
      system: campaign.system, name: name.trim(), kind,
      ancestry: ancestry.trim(), className, level,
    });
    await charactersRepo.update(created.id, {
      ...built,
      notes: background.trim() ? `Background: ${background.trim()}` : '',
      ...(selectedAncestry?.speed ? { speed: selectedAncestry.speed } : {}),
    });
    onClose();
    void navigate({ to: '/characters/$characterId', params: { characterId: created.id } });
  };

  const isLast = step === STEPS.length - 1;

  return (
    <Modal
      open
      onClose={onClose}
      title={`New character — ${STEPS[step]}`}
      className="max-w-2xl"
      footer={
        <>
          <Button variant="ghost" onClick={onClose}>Cancel</Button>
          {step > 0 && <Button variant="secondary" onClick={() => setStep((s) => s - 1)}>Back</Button>}
          {isLast
            ? <Button variant="primary" onClick={finish}>Create character</Button>
            : <Button variant="primary" disabled={!stepValid[step]} onClick={() => setStep((s) => s + 1)}>Next</Button>}
        </>
      }
    >
      {/* Step indicator */}
      <ol className="mb-4 flex gap-1 text-xs">
        {STEPS.map((s, i) => (
          <li key={s} className={cn('flex-1 rounded-md px-2 py-1 text-center', i === step ? 'bg-accent text-accent-ink' : i < step ? 'bg-elevated text-ink' : 'bg-surface text-muted')}>
            {i + 1}. {s}
          </li>
        ))}
      </ol>

      {step === 0 && (
        <div className="space-y-3">
          <Field label="Name"><Input value={name} onChange={(e) => setName(e.target.value)} placeholder="Character name" /></Field>
          <div className="grid grid-cols-2 gap-3">
            <Field label="Type">
              <Select value={kind} onChange={(e) => setKind(e.target.value as Character['kind'])}>
                <option value="pc">Player character</option>
                <option value="npc">NPC</option>
              </Select>
            </Field>
            <Field label="Level"><NumberField value={level} min={1} max={20} onChange={setLevel} aria-label="Level" /></Field>
          </div>
          <div className="grid grid-cols-2 gap-3">
            <Field label="Class">
              <Select value={classChoice} onChange={(e) => setClassChoice(e.target.value)} aria-label="Class">
                {getClassNames(campaign.system).map((c) => <option key={c} value={c}>{c}</option>)}
                <option value="Custom">Custom…</option>
              </Select>
            </Field>
            {classChoice === 'Custom'
              ? <Field label="Custom class"><Input value={customClass} onChange={(e) => setCustomClass(e.target.value)} placeholder="Class name" /></Field>
              : <Field label="Hit die / HP"><Input value={def ? (campaign.system === 'pf2e' ? `${def.hitDie} HP/level` : `d${def.hitDie}`) : '—'} disabled /></Field>}
          </div>
          {classChoice === 'Custom' && campaign.system === '5e' && (
            <Field label="Hit die">
              <Select value={hitDie} onChange={(e) => setHitDie(Number(e.target.value))}>{HIT_DICE.map((d) => <option key={d} value={d}>d{d}</option>)}</Select>
            </Field>
          )}
          <div className="grid grid-cols-2 gap-3">
            <Field label="Ancestry / race">
              {campaign.system === 'pf2e' && ancestries.length > 0 ? (
                <Select value={ancestry} onChange={(e) => setAncestry(e.target.value)} aria-label="Ancestry">
                  <option value="">Choose…</option>
                  {ancestries.map((a) => <option key={a.name} value={a.name}>{a.name}</option>)}
                </Select>
              ) : (
                <Input value={ancestry} onChange={(e) => setAncestry(e.target.value)} placeholder="e.g. Human" />
              )}
            </Field>
            <Field label="Background (optional)"><Input value={background} onChange={(e) => setBackground(e.target.value)} placeholder="e.g. Acolyte" /></Field>
          </div>
          {def && <p className="text-xs text-muted">{classSummary(campaign.system, className)}</p>}
        </div>
      )}

      {step === 1 && (
        <div>
          <div className="mb-3 flex gap-1">
            {([['standard', 'Standard array'], ['pointbuy', 'Point buy'], ['roll', '4d6 drop lowest']] as const).map(([m, label]) => (
              <button key={m} onClick={() => chooseMethod(m)} className={cn('rounded-md px-3 py-1.5 text-sm', method === m ? 'bg-accent text-accent-ink' : 'bg-elevated text-muted hover:text-ink')}>{label}</button>
            ))}
          </div>
          {method === 'roll' && <Button size="sm" variant="secondary" className="mb-3" onClick={() => chooseMethod('roll')}>↻ Reroll</Button>}
          {method === 'pointbuy' && <p className={cn('mb-3 text-sm', pointBuyRemaining(pb) < 0 ? 'text-danger' : 'text-muted')}>Points remaining: <span className="font-semibold text-ink">{pointBuyRemaining(pb)}</span> / 27</p>}
          {def && <p className="mb-3 text-xs text-muted">Suggested key {def.keyAbilities.length > 1 ? 'abilities' : 'ability'}: <span className="text-ink">{def.keyAbilities.map((k) => ABILITY_ABBR[k]).join(' / ')}</span></p>}
          {usesPool && !poolValid && <p className="mb-2 text-sm text-warning">Assign each value to a different ability.</p>}
          <div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
            {ABILITIES.map((a, i) => {
              const value = usesPool ? (pool[assignment[i]!] ?? 0) : pb[i]!;
              const isKey = def?.keyAbilities.includes(a);
              return (
                <div key={a} className={cn('rounded-lg border bg-surface p-3 text-center', isKey ? 'border-accent/60' : 'border-line')}>
                  <div className="text-xs font-semibold uppercase tracking-wide text-muted">{ABILITY_ABBR[a]}{isKey ? ' ★' : ''}</div>
                  {usesPool ? (
                    <Select className="mt-1" aria-label={`${ABILITY_ABBR[a]} value`} value={assignment[i]} onChange={(e) => setAssignment((prev) => prev.map((v, j) => (j === i ? Number(e.target.value) : v)))}>
                      {pool.map((p, idx) => <option key={idx} value={idx} disabled={assignment.includes(idx) && assignment[i] !== idx}>{p}</option>)}
                    </Select>
                  ) : (
                    <NumberField className="mt-1" value={pb[i]!} min={POINT_BUY_MIN} max={POINT_BUY_MAX} aria-label={`${ABILITY_ABBR[a]} score`} onChange={(v) => setPb((prev) => prev.map((x, j) => (j === i ? Math.max(POINT_BUY_MIN, Math.min(POINT_BUY_MAX, v)) : x)))} />
                  )}
                  <div className="mt-1 text-xs text-accent">{formatModifier(abilityModifier(value))}</div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {step === 2 && (
        <div>
          <p className="mb-3 text-sm text-muted">Choose <span className="font-semibold text-ink">{Math.min(skillCount, skillOptions.length)}</span> trained {skillCount === 1 ? 'skill' : 'skills'} ({skills.length} selected).</p>
          <div className="grid grid-cols-2 gap-2 sm:grid-cols-3">
            {skillOptions.map((key) => {
              const checked = skills.includes(key);
              const disabled = !checked && skills.length >= skillCount;
              return (
                <label key={key} className={cn('flex items-center gap-2 rounded-md border px-3 py-2 text-sm', checked ? 'border-accent/60 bg-accent/5 text-ink' : disabled ? 'border-line text-muted opacity-50' : 'border-line text-ink')}>
                  <input type="checkbox" checked={checked} disabled={disabled} onChange={() => toggleSkill(key)} />
                  {skillLabel(key)}
                </label>
              );
            })}
          </div>
        </div>
      )}

      {step === 3 && (
        <div className="space-y-3 text-sm">
          <div className="rounded-lg border border-line bg-surface p-3">
            <div className="font-medium text-ink">{name || 'Unnamed'} — level {level} {[ancestry, className].filter(Boolean).join(' ')}</div>
            <div className="mt-2 grid grid-cols-2 gap-2 text-center">
              <Stat label="Max HP" value={built.hp.max} />
              <Stat label="AC" value={sys.baseArmorClass({ level, abilities, armorBonus: 0 })} />
            </div>
          </div>
          <Review label="Ability scores" value={ABILITIES.map((a) => `${ABILITY_ABBR[a]} ${abilities[a]}`).join('  ')} />
          <Review label="Trained saves" value={Object.keys(built.saveRanks).map((k) => k.toUpperCase()).join(', ') || '—'} />
          <Review label="Trained skills" value={Object.keys(built.skillRanks).map(skillLabel).join(', ') || '—'} />
          {built.spellcasting.slots.length > 0 && (
            <Review label="Spell slots" value={built.spellcasting.slots.map((s) => `${s.max}×L${s.level}`).join('  ')} />
          )}
          {built.spellcasting.pact && <Review label="Pact slots" value={`${built.spellcasting.pact.max}× L${built.spellcasting.pact.level}`} />}
          <p className="text-xs text-muted">
            You can add equipment, spells known, class features, and resources on the sheet next.
          </p>
        </div>
      )}
    </Modal>
  );
}

function classSummary(system: Campaign['system'], className: string): string {
  const def = getClassDef(system, className);
  if (!def) return '';
  const caster = def.caster === 'none' ? 'non-caster' : def.caster === 'pact' ? 'pact caster' : `${def.caster} caster`;
  return `${className}: ${system === 'pf2e' ? `${def.hitDie} HP/level` : `d${def.hitDie} hit die`}, ${caster}.`;
}

function Stat({ label, value }: { label: string; value: number | string }) {
  return (
    <div className="rounded-md border border-line bg-panel p-2">
      <div className="font-display text-lg font-semibold text-accent">{value}</div>
      <div className="text-xs uppercase tracking-wide text-muted">{label}</div>
    </div>
  );
}

function Review({ label, value }: { label: string; value: string }) {
  return (
    <div className="flex gap-2">
      <span className="w-28 shrink-0 text-xs uppercase tracking-wide text-muted">{label}</span>
      <span className="text-ink">{value}</span>
    </div>
  );
}
