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

interface Combatant {
  id: string;
  name: string;
  initiative: number;
  hp: number;
  maxHp: number;
  conditions: string[];
  notes?: string;
  // ponytail: 5e death saves — 3 successes stabilize, 3 failures = dead.
  deathSaves?: { successes: number; failures: number };
}

const CONDITIONS = [
  "Blinded", "Charmed", "Deafened", "Frightened", "Grappled",
  "Incapacitated", "Invisible", "Paralyzed", "Poisoned", "Prone",
  "Restrained", "Stunned", "Unconscious",
];

const TURN_SECONDS = 60;

let nextId = 1;

export function InitiativeTracker() {
  // ponytail: persist combatants, round, and active turn so a refresh
  // mid-combat doesn't lose the tracker. nextId stays module-level (cosmetic).
  const [combatants, setCombatants] = usePersistentState<Combatant[]>("initiative.combatants", []);
  const [name, setName] = useState("");
  const [initBonus, setInitBonus] = useState(0);
  const [hp, setHp] = useState(10);
  const [activeId, setActiveId] = usePersistentState<string | null>("initiative.activeId", null);
  const [round, setRound] = usePersistentState<number>("initiative.round", 1);
  const [confirmReset, setConfirmReset] = useState(false);
  const [hpInput, setHpInput] = useState<Record<string, string>>({});
  // ponytail: per-combatant UI flags. Live in component state, not persisted.
  const [condInput, setCondInput] = useState<Record<string, string>>({});
  const [condEditing, setCondEditing] = useState<string | null>(null);
  const [notesEditing, setNotesEditing] = useState<string | null>(null);
  // ponytail: custom conditions the DM has added, persisted so they reuse
  // across sessions (e.g. "Concentrating", "Raging", "Hexed").
  const [customConditions, setCustomConditions] = usePersistentState<string[]>("initiative.customConditions", []);
  // ponytail: optional turn timer — counts down from TURN_SECONDS, resets on
  // Next Turn, turns red at 0. Off until the DM starts it.
  const [timerOn, setTimerOn] = useState(false);
  const [timerLeft, setTimerLeft] = useState(TURN_SECONDS);
  const { addToast } = useToast();

  // ponytail: listen for EncounterBuilder's "send to initiative" event.
  // We don't auto-switch view (the DM might be editing) but the toast tells
  // them where the combatants landed. The encounter's HP estimates land in
  // a new "pending" state — the DM can then click "Roll initiative" to assign
  // d20+0 to each (since the LLM didn't provide DEX scores) and re-sort.
  const [pendingPush, setPendingPush] = useState<{ count: number; source: string } | null>(null);
  useEffect(() => {
    return bus.on<AddCombatantsPayload>(Events.AddCombatants, (payload) => {
      const added: Combatant[] = payload.combatants.map((c) => ({
        id: String(nextId++),
        name: c.name,
        initiative: 0,
        hp: c.hp,
        maxHp: c.hp,
        conditions: [],
      }));
      setCombatants((prev) => [...prev, ...added]);
      setPendingPush({ count: added.length, source: payload.source });
    });
  }, []);

  // ponytail: roll initiative for any combatant stuck at 0 (the placeholder
  // for "we don't know the DEX yet"). The DM would otherwise have to click
  // each one individually. After this, sort by initiative.
  function rollPendingInitiative() {
    setCombatants((prev) => {
      const rolled = prev.map((c) =>
        c.initiative === 0 && pendingPush && c.id
          ? { ...c, initiative: Math.floor(Math.random() * 20) + 1 + 0 }
          : c,
      );
      return rolled.sort((a, b) => b.initiative - a.initiative);
    });
    setPendingPush(null);
  }

  const addCombatant = useCallback(() => {
    if (!name.trim()) return;
    const roll = Math.floor(Math.random() * 20) + 1 + initBonus;
    const c: Combatant = {
      id: String(nextId++),
      name: name.trim(),
      initiative: roll,
      hp,
      maxHp: hp,
      conditions: [],
    };
    setCombatants((prev) => [...prev, c].sort((a, b) => b.initiative - a.initiative));
    setName("");
  }, [name, initBonus, hp]);

  const removeCombatant = useCallback((id: string) => {
    setCombatants((prev) => prev.filter((c) => c.id !== id));
    setActiveId((prev) => (prev === id ? null : prev));
  }, []);

  const toggleCondition = useCallback((id: string, condition: string) => {
    setCombatants((prev) =>
      prev.map((c) =>
        c.id === id
          ? {
              ...c,
              conditions: c.conditions.includes(condition)
                ? c.conditions.filter((cn) => cn !== condition)
                : [...c.conditions, condition],
            }
          : c,
      ),
    );
  }, []);

  // ponytail: ±1 buttons are for chipping; typed input is for big hits.
  // Setting HP to a specific number requires a single keystroke + Enter.
  const applyHp = useCallback(
    (id: string, raw: string) => {
      const n = parseInt(raw);
      if (Number.isNaN(n)) return;
      setCombatants((prev) =>
        prev.map((c) =>
          c.id === id ? { ...c, hp: Math.max(0, Math.min(c.maxHp, n)) } : c,
        ),
      );
      setHpInput((prev) => {
        const { [id]: _, ...rest } = prev;
        return rest;
      });
    },
    [],
  );

  const changeHp = useCallback((id: string, delta: number) => {
    setCombatants((prev) =>
      prev.map((c) =>
        c.id === id ? { ...c, hp: Math.max(0, Math.min(c.maxHp, c.hp + delta)) } : c,
      ),
    );
  }, []);

  const setInitiative = useCallback((id: string, raw: string) => {
    const n = parseInt(raw);
    if (Number.isNaN(n)) return;
    setCombatants((prev) =>
      [...prev.map((c) => (c.id === id ? { ...c, initiative: n } : c))].sort(
        (a, b) => b.initiative - a.initiative,
      ),
    );
  }, []);

  const nextTurn = useCallback(() => {
    if (combatants.length === 0) return;
    if (!activeId) {
      setActiveId(combatants[0].id);
      return;
    }
    const idx = combatants.findIndex((c) => c.id === activeId);
    if (idx === combatants.length - 1) {
      setRound((r) => r + 1);
      setActiveId(combatants[0].id);
    } else {
      setActiveId(combatants[idx + 1].id);
    }
    setTimerLeft(TURN_SECONDS); // ponytail: reset the per-turn timer.
  }, [combatants, activeId]);

  // ponytail: tick the timer once per second while armed. Stopping at 0 lets
  // the red zero sit until the DM advances (don't auto-advance — that would
  // steal a player's turn).
  useEffect(() => {
    if (!timerOn) return;
    if (timerLeft <= 0) return;
    const t = setTimeout(() => setTimerLeft((s) => s - 1), 1000);
    return () => clearTimeout(t);
  }, [timerOn, timerLeft]);

  // ponytail: 5e death save. d20: 10+ = success, <10 = failure, nat 20 = 2
  // successes, nat 1 = 2 failures. 3 successes stabilize, 3 failures = dead.
  function rollDeathSave(id: string) {
    const roll = Math.floor(Math.random() * 20) + 1;
    setCombatants((prev) =>
      prev.map((c) => {
        if (c.id !== id) return c;
        const ds = c.deathSaves ?? { successes: 0, failures: 0 };
        let succ = 0;
        let fail = 0;
        if (roll === 20) succ += 2;
        else if (roll === 1) fail += 2;
        else if (roll >= 10) succ += 1;
        else fail += 1;
        const next = { successes: ds.successes + succ, failures: ds.failures + fail };
        return { ...c, deathSaves: next };
      }),
    );
    addToast(`Death save: ${roll}`);
  }

  function adjustDeathSave(id: string, kind: "successes" | "failures", delta: number) {
    setCombatants((prev) =>
      prev.map((c) => {
        if (c.id !== id) return c;
        const ds = c.deathSaves ?? { successes: 0, failures: 0 };
        return { ...c, deathSaves: { ...ds, [kind]: Math.max(0, Math.min(3, ds[kind] + delta)) } };
      }),
    );
  }

  function addCustomCondition(id: string, raw: string) {
    const cond = raw.trim();
    if (!cond) return;
    toggleCondition(id, cond);
    if (!customConditions.includes(cond) && !CONDITIONS.includes(cond)) {
      setCustomConditions((prev) => [...prev, cond]);
    }
    setCondInput((p) => { const { [id]: _, ...r } = p; return r; });
    setCondEditing(null);
  }

  function setNotes(id: string, raw: string) {
    setCombatants((prev) => prev.map((c) => (c.id === id ? { ...c, notes: raw } : c)));
  }

  const reset = useCallback(() => {
    setCombatants([]);
    setActiveId(null);
    setRound(1);
    setConfirmReset(false);
  }, []);

  const activeIdx = activeId ? combatants.findIndex((c) => c.id === activeId) : -1;
  const nextUpId =
    combatants.length > 0
      ? activeIdx === -1
        ? combatants[0].id
        : activeIdx < combatants.length - 1
          ? combatants[activeIdx + 1].id
          : null
      : null;

  return (
    <div className="flex flex-col gap-2 h-full overflow-y-auto">
      {/* Round indicator */}
      <div className="flex items-center justify-between">
        <span className="text-[var(--color-gold-bright)] font-heading text-xs font-semibold">
          Round {round}
        </span>
        <div className="flex gap-1 items-center">
          {/* ponytail: optional per-turn timer. Toggle to arm; resets on Next Turn. */}
          {activeId && (
            <button
              onClick={() => { setTimerOn((o) => !o); if (!timerOn) setTimerLeft(TURN_SECONDS); }}
              className={`rounded px-1.5 py-0.5 text-[10px] font-mono cursor-pointer border transition-colors ${
                timerOn
                  ? timerLeft === 0
                    ? "bg-[var(--color-danger)]/20 border-[var(--color-danger)] text-[var(--color-danger)]"
                    : "border-[var(--color-gold-bright)] text-[var(--color-gold-bright)]"
                  : "border-[var(--color-border-subtle)] text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)]"
              }`}
              title="Toggle turn timer"
              aria-label="Toggle turn timer"
            >
              {timerOn ? `${timerLeft}s` : "⏱"}
            </button>
          )}
          <button
            onClick={nextTurn}
            disabled={combatants.length === 0}
            className="rounded bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)] px-2 py-0.5 text-xs font-semibold cursor-pointer hover:bg-[var(--color-gold-muted)] transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
          >
            Next Turn
          </button>
          {confirmReset ? (
            <div className="flex items-center gap-1">
              <span className="text-[10px] text-[var(--color-danger)]">Clear all?</span>
              <button
                onClick={reset}
                className="rounded bg-[var(--color-danger)] text-white px-2 py-0.5 text-xs font-semibold cursor-pointer"
              >
                Yes
              </button>
              <button
                onClick={() => setConfirmReset(false)}
                className="rounded bg-[var(--color-bg-surface)] border border-[var(--color-border-subtle)] text-[var(--color-text-dim)] px-2 py-0.5 text-xs cursor-pointer"
              >
                No
              </button>
            </div>
          ) : (
            <button
              onClick={() => setConfirmReset(true)}
              disabled={combatants.length === 0}
              className="rounded bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-dim)] px-2 py-0.5 text-xs cursor-pointer hover:text-[var(--color-danger)] transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
            >
              Reset
            </button>
          )}
        </div>
      </div>

      {/* ponytail: when monsters are pushed from the Encounter Builder, the
          DM needs to roll initiative for them. The push sets all their
          initiatives to 0; this banner offers a one-click 1d20 per pending
          combatant. */}
      {pendingPush && (
        <div className="flex items-center justify-between rounded-lg bg-[var(--color-gold-glow)] border border-[var(--color-gold-bright)]/40 px-2.5 py-1.5 text-xs">
          <span className="text-[var(--color-gold-bright)]">
            {pendingPush.count} from “{pendingPush.source}”
          </span>
          <button
            onClick={rollPendingInitiative}
            className="rounded bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)] px-2 py-0.5 text-[10px] font-semibold cursor-pointer"
          >
            Roll initiative
          </button>
        </div>
      )}

      {/* Add combatant */}
      <div className="flex gap-1.5">
        <input
          className="flex-1 min-w-0 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-2 py-1 text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none focus:border-[var(--color-gold-bright)] text-xs"
          value={name}
          onChange={(e) => setName(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && addCombatant()}
          placeholder="Name"
          aria-label="Combatant name"
        />
        <input
          className="w-10 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-1 py-1 text-center text-[var(--color-text-primary)] focus:outline-none focus:border-[var(--color-gold-bright)] text-xs font-mono"
          type="number"
          value={initBonus}
          onChange={(e) => setInitBonus(parseInt(e.target.value) || 0)}
          title="Initiative bonus"
          aria-label="Initiative bonus"
        />
        <input
          className="w-12 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-1 py-1 text-center text-[var(--color-text-primary)] focus:outline-none focus:border-[var(--color-gold-bright)] text-xs font-mono"
          type="number"
          value={hp}
          onChange={(e) => setHp(parseInt(e.target.value) || 1)}
          title="Max HP"
          aria-label="Max HP"
        />
        <button
          onClick={addCombatant}
          className="rounded-lg bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)] px-2 py-1 text-xs font-semibold cursor-pointer hover:bg-[var(--color-gold-muted)] transition-colors"
        >
          +
        </button>
      </div>

      {/* Empty state */}
      {combatants.length === 0 && (
        <div className="flex flex-col items-center justify-center py-6 text-center text-[var(--color-text-dim)] text-xs">
          <span>No combatants yet.</span>
          <span className="mt-1">Add a name + HP, press Enter or +.</span>
        </div>
      )}

      {/* Combatant list */}
      <div className="flex flex-col gap-1 flex-1 overflow-y-auto">
        {combatants.map((c) => {
          const isActive = c.id === activeId;
          const isNextUp = c.id === nextUpId && !isActive;
          const hpPct = c.maxHp > 0 ? c.hp / c.maxHp : 0;
          const hpColor =
            hpPct > 0.5
              ? "var(--color-success)"
              : hpPct > 0.25
                ? "var(--color-gold-bright)"
                : "var(--color-danger)";

          return (
            <div
              key={c.id}
              className={`rounded-lg p-2 text-xs transition-all cursor-default ${
                isActive
                  ? "glass-card-active"
                  : isNextUp
                    ? "bg-[var(--color-bg-card)] border border-[var(--color-border-glass)]"
                    : "bg-[var(--color-bg-surface)] border border-[var(--color-border-subtle)]"
              }`}
            >
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2 min-w-0">
                  <input
                    className="font-mono text-[var(--color-text-dim)] w-8 text-right shrink-0 bg-transparent focus:outline-none focus:text-[var(--color-gold-bright)]"
                    type="number"
                    defaultValue={c.initiative}
                    onBlur={(e) => setInitiative(c.id, e.target.value)}
                    onKeyDown={(e) => e.key === "Enter" && (e.target as HTMLInputElement).blur()}
                    title="Edit initiative"
                    aria-label={`Initiative for ${c.name}`}
                  />
                  <span className="text-[var(--color-text-primary)] truncate font-medium">
                    {c.name}
                  </span>
                  {isNextUp && (
                    <span className="text-[9px] uppercase tracking-wider text-[var(--color-gold-muted)] shrink-0">
                      next
                    </span>
                  )}
                </div>
                <button
                  onClick={() => removeCombatant(c.id)}
                  className="text-[var(--color-text-dim)] hover:text-[var(--color-danger)] transition-colors shrink-0 cursor-pointer"
                  title="Remove"
                  aria-label={`Remove ${c.name}`}
                >
                  ×
                </button>
              </div>

              {/* HP bar */}
              <div className="flex items-center gap-1.5 mt-1">
                <div className="flex-1 h-1.5 rounded-full bg-[var(--color-bg-deep)] overflow-hidden">
                  <div
                    className="h-full rounded-full transition-all duration-300"
                    style={{ width: `${hpPct * 100}%`, backgroundColor: hpColor }}
                  />
                </div>
                <span className="font-mono text-[10px] shrink-0" style={{ color: hpColor }}>
                  {c.hp}/{c.maxHp}
                </span>
                <button onClick={() => changeHp(c.id, -1)} className="text-[var(--color-text-dim)] hover:text-[var(--color-danger)] cursor-pointer" aria-label="Decrease HP by 1">−</button>
                <button onClick={() => changeHp(c.id, 1)} className="text-[var(--color-text-dim)] hover:text-[var(--color-success)] cursor-pointer" aria-label="Increase HP by 1">+</button>
              </div>

              {/* HP direct-set input — type a number, press Enter to set HP. */}
              <div className="flex gap-1 mt-1">
                <input
                  className="w-14 rounded bg-[var(--color-bg-deep)] border border-[var(--color-border-subtle)] px-1.5 py-0.5 text-[10px] text-[var(--color-text-primary)] focus:outline-none focus:border-[var(--color-gold-bright)] font-mono"
                  type="number"
                  placeholder="set HP"
                  value={hpInput[c.id] ?? ""}
                  onChange={(e) => setHpInput((p) => ({ ...p, [c.id]: e.target.value }))}
                  onKeyDown={(e) => e.key === "Enter" && hpInput[c.id] && applyHp(c.id, hpInput[c.id])}
                  title="Type a number and press Enter to set HP"
                  aria-label={`Set HP for ${c.name}`}
                />
                <span className="text-[9px] text-[var(--color-text-dim)] self-center">set</span>
              </div>

              {/* Death saves — only when downed (HP 0). 5e: 3 succ stabilize, 3 fail = dead. */}
              {c.hp === 0 && (
                <div className="flex items-center gap-1 mt-1">
                  <span className="text-[9px] text-[var(--color-text-dim)] shrink-0">Deaths:</span>
                  {[0, 1, 2].map((i) => (
                    <button
                      key={`s${i}`}
                      onClick={() => adjustDeathSave(c.id, "successes", ((c.deathSaves?.successes ?? 0) > i) ? -1 : 1)}
                      className="w-3 h-3 rounded-full border text-[8px] leading-none cursor-pointer"
                      style={{
                        backgroundColor: (c.deathSaves?.successes ?? 0) > i ? "var(--color-success)" : "transparent",
                        borderColor: "var(--color-success)",
                      }}
                      aria-label="Death save success"
                    />
                  ))}
                  <span className="text-[var(--color-text-dim)] mx-0.5">/</span>
                  {[0, 1, 2].map((i) => (
                    <button
                      key={`f${i}`}
                      onClick={() => adjustDeathSave(c.id, "failures", ((c.deathSaves?.failures ?? 0) > i) ? -1 : 1)}
                      className="w-3 h-3 rounded-full border text-[8px] leading-none cursor-pointer"
                      style={{
                        backgroundColor: (c.deathSaves?.failures ?? 0) > i ? "var(--color-danger)" : "transparent",
                        borderColor: "var(--color-danger)",
                      }}
                      aria-label="Death save failure"
                    />
                  ))}
                  <button
                    onClick={() => rollDeathSave(c.id)}
                    className="ml-1 rounded bg-[var(--color-bg-deep)] border border-[var(--color-border-subtle)] px-1.5 py-0 text-[9px] text-[var(--color-text-secondary)] hover:text-[var(--color-gold-bright)] cursor-pointer"
                    title="Roll a death save (d20)"
                  >
                    🎲
                  </button>
                </div>
              )}

              {/* Conditions */}
              {c.conditions.length > 0 && (
                <div className="flex flex-wrap gap-0.5 mt-1">
                  {c.conditions.map((cn) => (
                    <span
                      key={cn}
                      onClick={() => toggleCondition(c.id, cn)}
                      className="rounded-full bg-[var(--color-gold-glow)] text-[var(--color-gold-bright)] px-1.5 py-0 text-[10px] cursor-pointer hover:opacity-80"
                      title={`Remove ${cn}`}
                    >
                      {cn}
                    </span>
                  ))}
                </div>
              )}

              {/* Add condition — standard 13 + custom, plus a free-text input. */}
              {condEditing === c.id ? (
                <div className="flex gap-1 mt-1">
                  <input
                    className="flex-1 min-w-0 rounded bg-[var(--color-bg-deep)] border border-[var(--color-gold-bright)] px-1.5 py-0.5 text-[10px] text-[var(--color-text-primary)] focus:outline-none"
                    placeholder="Custom condition…"
                    value={condInput[c.id] ?? ""}
                    onChange={(e) => setCondInput((p) => ({ ...p, [c.id]: e.target.value }))}
                    onKeyDown={(e) => {
                      if (e.key === "Enter") addCustomCondition(c.id, condInput[c.id] ?? "");
                      if (e.key === "Escape") setCondEditing(null);
                    }}
                    autoFocus
                    aria-label="Custom condition"
                  />
                  {customConditions.filter((cn) => !c.conditions.includes(cn)).slice(0, 4).map((cn) => (
                    <button key={cn} onClick={() => addCustomCondition(c.id, cn)} className="rounded bg-[var(--color-bg-deep)] text-[var(--color-text-dim)] px-1 py-0 text-[9px] cursor-pointer hover:text-[var(--color-text-secondary)]">{cn}</button>
                  ))}
                </div>
              ) : (
                <div className="flex flex-wrap gap-0.5 mt-1">
                  {CONDITIONS.filter((cn) => !c.conditions.includes(cn)).map((cn) => (
                    <button
                      key={cn}
                      onClick={() => toggleCondition(c.id, cn)}
                      className="rounded bg-[var(--color-bg-deep)] text-[var(--color-text-dim)] px-1 py-0 text-[9px] hover:text-[var(--color-text-secondary)] cursor-pointer transition-colors"
                      title={`Add ${cn}`}
                    >
                      +{cn.slice(0, 3)}
                    </button>
                  ))}
                  {customConditions.filter((cn) => !c.conditions.includes(cn) && !CONDITIONS.includes(cn)).map((cn) => (
                    <button
                      key={cn}
                      onClick={() => toggleCondition(c.id, cn)}
                      className="rounded bg-[var(--color-bg-deep)] text-[var(--color-gold-muted)] px-1 py-0 text-[9px] hover:text-[var(--color-gold-bright)] cursor-pointer transition-colors"
                      title={`Add ${cn}`}
                    >
                      +{cn.slice(0, 3)}
                    </button>
                  ))}
                  <button
                    onClick={() => setCondEditing(c.id)}
                    className="rounded bg-[var(--color-bg-deep)] text-[var(--color-text-dim)] px-1 py-0 text-[9px] hover:text-[var(--color-gold-bright)] cursor-pointer transition-colors"
                    title="Add a custom condition"
                  >
                    +…
                  </button>
                </div>
              )}

              {/* Notes per combatant — table memory for concentration, movement, etc. */}
              <div className="mt-1">
                {notesEditing === c.id ? (
                  <textarea
                    className="w-full rounded bg-[var(--color-bg-deep)] border border-[var(--color-gold-bright)] px-1.5 py-1 text-[10px] text-[var(--color-text-primary)] focus:outline-none resize-y min-h-12"
                    placeholder="Notes (e.g. concentrating on Hold Person, 50ft move left)…"
                    value={c.notes ?? ""}
                    onChange={(e) => setNotes(c.id, e.target.value)}
                    onBlur={() => setNotesEditing(null)}
                    autoFocus
                    aria-label={`Notes for ${c.name}`}
                  />
                ) : (
                  <button
                    onClick={() => setNotesEditing(c.id)}
                    className={`text-[9px] cursor-pointer transition-colors ${c.notes ? "text-[var(--color-text-secondary)]" : "text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)]"}`}
                    title="Notes"
                  >
                    {c.notes ? `📝 ${c.notes}` : "📝 add note"}
                  </button>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}
