import { useEffect, useRef, type KeyboardEvent } from "react";
import { HEALER_ABILITIES } from "../../game/healers";
import {
  ABILITY_LOADOUT_SLOTS,
  MAX_ACTIVE_ROSTER,
  MAX_EQUIPPED_SPELLS,
  PARTY_DRAFT_WAVE_COUNT,
  PARTY_RECRUITS_PER_WAVE,
  SPELL_DRAFT_WAVE_COUNT,
  SPELL_PICKS_PER_WAVE,
  partyCompositionLabel,
  partyRolePresentation,
  rpgFocusId,
} from "../../game/rpgRoguelike";
import { DEFAULT_CONTROLLER_GLYPHS } from "../../input/controllerGlyphs";
import {
  ChallengeObjective,
  createUiContext,
  currentBoss,
  FocusButton,
  GearCard,
  GearStatComparison,
  gearComparisonLabel,
  PartyCard,
  rewardSummary,
  RoutePips,
  RunHeader,
  runAccentStyle,
  SpellCard,
  type RpgRunUiContext,
  type RpgRunUiProps,
} from "./RpgRunUiShared";
import { PartyRoleBadge } from "./PartyRoleBadge";
import "./rpgRoguelike.css";

function DraftFooter({ context, focusId, action, disabled, label, hint }: {
  readonly context: RpgRunUiContext;
  readonly focusId: string;
  readonly action: Parameters<RpgRunUiProps["onAction"]>[0];
  readonly disabled: boolean;
  readonly label: string;
  readonly hint: string;
}) {
  return (
    <footer className="rpg-draft-footer">
      <span>{hint}</span>
      <FocusButton
        context={context}
        focusId={focusId}
        command={{ type: "run-action", action }}
        className="rpg-primary-action"
        disabled={disabled}
      >
        {label}<b>{DEFAULT_CONTROLLER_GLYPHS.confirm}</b>
      </FocusButton>
    </footer>
  );
}

function PartyDraft({ context }: { context: RpgRunUiContext }) {
  const { run } = context;
  const draft = run.partyDraft!;
  const canRemove = draft.waveIndex > 0;
  const canRecruit = run.roster.length < MAX_ACTIVE_ROSTER
    && draft.recruitedThisWaveIds.length < PARTY_RECRUITS_PER_WAVE;
  const finalWave = draft.waveIndex === PARTY_DRAFT_WAVE_COUNT - 1;
  const canContinue = finalWave
    ? run.roster.length === MAX_ACTIVE_ROSTER && run.roster.some((member) => member.role === "Tank")
    : run.roster.length + (PARTY_DRAFT_WAVE_COUNT - draft.waveIndex - 1) * PARTY_RECRUITS_PER_WAVE >= MAX_ACTIVE_ROSTER;
  return (
    <section className="rpg-phase-panel rpg-party-draft" aria-labelledby="rpg-party-draft-title">
      <div className="rpg-phase-title">
        <div><small>Draft {draft.waveIndex + 1} / {PARTY_DRAFT_WAVE_COUNT}</small><h2 id="rpg-party-draft-title">Assemble your party</h2></div>
        <p>Recruit up to {PARTY_RECRUITS_PER_WAVE} this wave. Four companions enter each room.</p>
        <b>{draft.recruitedThisWaveIds.length}/{PARTY_RECRUITS_PER_WAVE} picked</b>
      </div>
      <div className="rpg-party-offer-grid">
        {draft.offers.map((candidate) => {
          const recruited = run.roster.some((member) => member.instanceId === candidate.candidateId);
          const disabled = !recruited && !canRecruit;
          const action = recruited
            ? { type: "party-remove", memberId: candidate.candidateId } as const
            : { type: "party-recruit", candidateId: candidate.candidateId } as const;
          return (
            <PartyCard
              key={candidate.candidateId}
              candidate={candidate}
              selected={recruited}
              action={disabled ? <span className="rpg-card-status">Party full</span> : (
                <FocusButton
                  context={context}
                  focusId={rpgFocusId.partyOffer(candidate.candidateId)}
                  command={{ type: "run-action", action }}
                  className="rpg-card-action"
                  disabled={recruited && !canRemove}
                  pressed={recruited}
                  label={`${recruited ? "Remove" : "Recruit"} ${candidate.name}, ${partyRolePresentation(candidate.role).label}`}
                ><span>{recruited ? canRemove ? "Remove" : "Locked" : "Recruit"}</span></FocusButton>
              )}
            />
          );
        })}
      </div>
      <div className="rpg-picked-strip" aria-label={`Current party, ${run.roster.length} of ${MAX_ACTIVE_ROSTER}: ${partyCompositionLabel(run.roster)}`}>
        <strong><span>Party {run.roster.length}/{MAX_ACTIVE_ROSTER}</span><small>{partyCompositionLabel(run.roster)}</small></strong>
        {run.roster.map((member) => (
          <FocusButton
            key={member.instanceId}
            context={context}
            focusId={rpgFocusId.partyMember(member.instanceId)}
            command={{ type: "run-action", action: { type: "party-remove", memberId: member.instanceId } }}
            className={`rpg-picked-chip rarity-${member.rarity}`}
            disabled={!canRemove}
            label={`Remove ${member.name}, ${partyRolePresentation(member.role).label}`}
          >
            <i style={{ background: member.color }} />
            <span className="rpg-picked-copy"><strong>{member.name}</strong><small>{member.className}</small></span>
            <PartyRoleBadge role={member.role} />
            <b className="rpg-picked-remove" aria-hidden="true">×</b>
          </FocusButton>
        ))}
        {Array.from({ length: Math.max(0, MAX_ACTIVE_ROSTER - run.roster.length) }, (_, index) => <i key={index} className="rpg-empty-chip">Open</i>)}
      </div>
      <DraftFooter
        context={context}
        focusId={rpgFocusId.partyContinue}
        action={{ type: "party-next-wave" }}
        disabled={!canContinue}
        label={finalWave ? "Lock Party" : "Reroll Offers"}
        hint={finalWave && !canContinue ? `Recruit exactly ${MAX_ACTIVE_ROSTER} companions, including a tank.` : "Previous recruits stay; remove any companion before locking the run."}
      />
    </section>
  );
}

function SpellDraft({ context }: { context: RpgRunUiContext }) {
  const { run } = context;
  const draft = run.spellDraft!;
  const canRemove = draft.waveIndex > 0;
  const canPick = run.selectedSpellIds.length < MAX_EQUIPPED_SPELLS
    && draft.pickedThisWaveIds.length < SPELL_PICKS_PER_WAVE;
  const finalWave = draft.waveIndex === SPELL_DRAFT_WAVE_COUNT - 1;
  const canContinue = !finalWave || run.selectedSpellIds.length > 0;
  return (
    <section className="rpg-phase-panel rpg-spell-draft" aria-labelledby="rpg-spell-draft-title">
      <div className="rpg-phase-title">
        <div><small>Draft {draft.waveIndex + 1} / {SPELL_DRAFT_WAVE_COUNT}</small><h2 id="rpg-spell-draft-title">Build your spellbook</h2></div>
        <p>Learn up to {SPELL_PICKS_PER_WAVE} spells. Abilities from every enabled healer class can mix.</p>
        <b>{draft.pickedThisWaveIds.length}/{SPELL_PICKS_PER_WAVE} picked</b>
      </div>
      <div className="rpg-spell-offer-grid">
        {draft.offers.map((spellId) => {
          const selected = run.selectedSpellIds.includes(spellId);
          const disabled = !selected && !canPick;
          const action = selected
            ? { type: "spell-remove", spellId } as const
            : { type: "spell-pick", spellId } as const;
          return (
            <SpellCard
              key={spellId}
              spellId={spellId}
              selected={selected}
              action={disabled ? <span className="rpg-card-status">Wave cap reached</span> : (
                <FocusButton
                  context={context}
                  focusId={rpgFocusId.spellOffer(spellId)}
                  command={{ type: "run-action", action }}
                  className="rpg-card-action"
                  disabled={selected && !canRemove}
                  pressed={selected}
                  label={`${selected ? "Remove" : "Learn"} ${HEALER_ABILITIES[spellId].name}`}
                ><span>{selected ? canRemove ? "Remove" : "Locked" : "Learn"}</span></FocusButton>
              )}
            />
          );
        })}
      </div>
      <div className="rpg-spellbook-strip" aria-label={`Spellbook, ${run.selectedSpellIds.length} of ${MAX_EQUIPPED_SPELLS}`}>
        <strong>Spellbook {run.selectedSpellIds.length}/{MAX_EQUIPPED_SPELLS}</strong>
        {ABILITY_LOADOUT_SLOTS.map((slotId, index) => ({ slotId, index, spellId: run.abilityLoadout[slotId] }))
          .filter((entry): entry is { slotId: typeof ABILITY_LOADOUT_SLOTS[number]; index: number; spellId: NonNullable<typeof entry.spellId> } => Boolean(entry.spellId))
          .map(({ spellId, index }) => {
          const spell = HEALER_ABILITIES[spellId];
          return (
            <FocusButton
              key={spellId}
              context={context}
              focusId={rpgFocusId.spellSelected(spellId)}
              command={{ type: "run-action", action: { type: "spell-remove", spellId } }}
              className="rpg-spellbook-chip"
              disabled={!canRemove}
              style={runAccentStyle(spell.color)}
              label={`Remove ${spell.name}`}
            ><b>{index + 1}</b><i>{spell.icon}</i><span>{spell.shortName}</span><small>×</small></FocusButton>
          );
        })}
      </div>
      <DraftFooter
        context={context}
        focusId={rpgFocusId.spellContinue}
        action={{ type: "spell-next-wave" }}
        disabled={!canContinue}
        label={finalWave ? "Lock Spellbook" : "Reroll Spells"}
        hint={draft.waveIndex > 0 ? "Remove drafted spells to make room for new magic." : "Each learned spell fills the next ability slot."}
      />
    </section>
  );
}

function Briefing({ context, kind }: { context: RpgRunUiContext; kind: "challenge" | "boss" }) {
  const { run } = context;
  const boss = currentBoss(run);
  if (kind === "challenge") {
    return (
      <section className="rpg-phase-panel rpg-centered-panel" aria-labelledby="rpg-challenge-title">
        <span className="rpg-hero-sigil">◇</span>
        <small>Hallway challenge</small>
        <h2 id="rpg-challenge-title">Prove the party before the next door</h2>
        <ChallengeObjective run={run} />
        <p className="rpg-soft-fail-copy">Failure does not end the run. Success adds gold and improves the next chest.</p>
        <FocusButton context={context} focusId={rpgFocusId.challengeStart} command={{ type: "run-action", action: { type: "challenge-start" } }} className="rpg-primary-action rpg-large-action">
          Start Challenge <b>{DEFAULT_CONTROLLER_GLYPHS.confirm}</b>
        </FocusButton>
      </section>
    );
  }
  return (
    <section className="rpg-phase-panel rpg-centered-panel rpg-boss-briefing" style={boss ? runAccentStyle(boss.accent) : undefined} aria-labelledby="rpg-boss-title">
      <span className="rpg-hero-sigil">{boss?.icon ?? "♛"}</span>
      <small>{run.bossIndex === run.bossRoute.length - 1 ? "Final encounter" : "Boss chamber"}</small>
      <h2 id="rpg-boss-title">{boss?.name ?? "Unknown Guardian"}</h2>
      <h3>{boss?.title}</h3>
      <p>{boss?.briefing}</p>
      {run.lastChallengeResult && (
        <div className={`rpg-challenge-result ${run.lastChallengeResult.succeeded ? "is-success" : "is-failure"}`}>
          {run.lastChallengeResult.succeeded ? `Challenge cleared · +${run.lastChallengeResult.objective.rewardCurrency} gold · upgraded chest` : "Challenge missed · standard chest remains"}
        </div>
      )}
      <FocusButton context={context} focusId={rpgFocusId.bossStart} command={{ type: "run-action", action: { type: "boss-start" } }} className="rpg-primary-action rpg-large-action">
        Enter Chamber <b>{DEFAULT_CONTROLLER_GLYPHS.confirm}</b>
      </FocusButton>
    </section>
  );
}

function BossCleared({ context }: { context: RpgRunUiContext }) {
  const boss = currentBoss(context.run);
  return (
    <section className="rpg-phase-panel rpg-cleared-panel" style={boss ? runAccentStyle(boss.accent) : undefined} aria-labelledby="rpg-cleared-title">
      <div className="rpg-exit-arrow"><i />↑<span>North gate open</span></div>
      <small>Boss defeated</small>
      <h2 id="rpg-cleared-title">{boss?.name} has fallen</h2>
      <p>Walk through the far door to claim the chest. Party health carries forward.</p>
      <FocusButton context={context} focusId={rpgFocusId.rewardOpen} command={{ type: "run-action", action: { type: "reward-open" } }} className="rpg-primary-action rpg-large-action">
        Open Chest <b>{DEFAULT_CONTROLLER_GLYPHS.confirm}</b>
      </FocusButton>
    </section>
  );
}

function Rewards({ context }: { context: RpgRunUiContext }) {
  const chest = context.run.pendingReward;
  if (!chest) return null;
  return (
    <section className="rpg-phase-panel rpg-reward-panel" aria-labelledby="rpg-reward-title">
      <div className="rpg-phase-title">
        <div><small>Chest quality +{chest.quality}</small><h2 id="rpg-reward-title">Choose one reward</h2></div>
        <p>Rewards improve this run only. Gear auto-equips when stronger.</p>
      </div>
      <div className="rpg-reward-grid">
        {chest.choices.map((choice) => {
          const summary = rewardSummary(choice);
          return (
            <FocusButton
              key={choice.id}
              context={context}
              focusId={rpgFocusId.rewardChoice(choice.id)}
              command={{ type: "run-action", action: { type: "reward-choose", choiceId: choice.id } }}
              className="rpg-reward-card"
              style={runAccentStyle(summary.accent)}
              label={choice.kind === "run-gear" ? `Claim ${choice.item.name}. ${gearComparisonLabel(context.run, choice.item)}` : `Claim ${choice.label}. ${summary.detail}`}
            >
              <i>{summary.icon}</i><small>{summary.eyebrow}</small><h3>{choice.label}</h3><p>{summary.detail}</p>
              {choice.kind === "run-gear" && <GearStatComparison run={context.run} item={choice.item} />}
              <b>Claim</b>
            </FocusButton>
          );
        })}
      </div>
      <footer className="rpg-controller-hint"><b>← / →</b> Choose <i /> <b>{DEFAULT_CONTROLLER_GLYPHS.confirm}</b> Claim</footer>
    </section>
  );
}

function Shop({ context }: { context: RpgRunUiContext }) {
  const { run } = context;
  const shop = run.shop;
  if (!shop) return null;
  const needsRest = run.playerHp > 0 && run.playerHp < 100
    || run.roster.some((member) => member.hp > 0 && member.hp < member.stats.maxHp);
  const dead = run.roster.filter((member) => member.hp <= 0);
  return (
    <section className="rpg-phase-panel rpg-shop-panel" aria-labelledby="rpg-shop-title">
      <div className="rpg-phase-title">
        <div><small>Safe intermission</small><h2 id="rpg-shop-title">Wayfarer's Exchange</h2></div>
        <p>Run gear auto-equips when stronger. Sell displaced gear from the bag.</p>
      </div>
      <div className="rpg-shop-layout">
        <section><h3>Buy gear</h3><div className="rpg-shop-grid">
          {shop.offers.map((offer) => {
            const disabled = offer.sold || run.currency < offer.price;
            return (
              <GearCard key={offer.id} run={run} item={offer.item} price={offer.price} action={
                <FocusButton
                  context={context}
                  focusId={rpgFocusId.shopOffer(offer.id)}
                  command={{ type: "run-action", action: { type: "shop-buy", offerId: offer.id } }}
                  className="rpg-card-action"
                  disabled={disabled}
                  label={`${offer.sold ? `${offer.item.name} sold` : `Buy ${offer.item.name} for ${offer.price} gold`}. ${gearComparisonLabel(run, offer.item)}`}
                ><span>{offer.sold ? "Sold" : run.currency < offer.price ? "Need gold" : "Buy"}</span></FocusButton>
              } />
            );
          })}
        </div></section>
        <section><h3>Bag & services</h3><div className="rpg-shop-side-list">
          {run.bag.length ? run.bag.map((item) => (
            <FocusButton key={item.id} context={context} focusId={rpgFocusId.shopSell(item.id)} command={{ type: "run-action", action: { type: "shop-sell", itemId: item.id } }} className="rpg-shop-row">
              <span><small>{item.slotId} · +{item.enhancement}</small>{item.name}</span><b>Sell ◆ {item.sellPrice}</b>
            </FocusButton>
          )) : <p className="rpg-empty-copy">Bag empty. Replaced gear appears here.</p>}
          <FocusButton context={context} focusId={rpgFocusId.shopRest} command={{ type: "run-action", action: { type: "shop-rest" } }} className="rpg-shop-row" disabled={!needsRest || run.currency < shop.restCost}>
            <span><small>Living members</small>Rest party</span><b>◆ {shop.restCost}</b>
          </FocusButton>
          {dead.map((member) => (
            <FocusButton key={member.instanceId} context={context} focusId={rpgFocusId.shopRevive(member.instanceId)} command={{ type: "run-action", action: { type: "shop-revive", memberId: member.instanceId } }} className="rpg-shop-row" disabled={run.currency < shop.reviveCost}>
              <span><small>Return at 50% HP</small>Revive {member.name}</span><b>◆ {shop.reviveCost}</b>
            </FocusButton>
          ))}
        </div></section>
      </div>
      <DraftFooter context={context} focusId={rpgFocusId.shopLeave} action={{ type: "shop-leave" }} disabled={false} label="Leave Shop" hint="Next act begins after leaving." />
    </section>
  );
}

function Terminal({ context }: { context: RpgRunUiContext }) {
  const victory = context.run.phase === "victory";
  return (
    <section className={`rpg-phase-panel rpg-centered-panel rpg-terminal ${victory ? "is-victory" : "is-defeat"}`} aria-labelledby="rpg-terminal-title">
      <span className="rpg-hero-sigil">{victory ? "♛" : "◇"}</span>
      <small>{victory ? "Expedition complete" : "Run ended"}</small>
      <h2 id="rpg-terminal-title">{victory ? "The gauntlet is conquered" : "The party has fallen"}</h2>
      <p>{context.run.bossesDefeated} bosses defeated · {context.run.selectedSpellIds.length} spells drafted · ◆ {context.run.currency} remaining</p>
      <div className="rpg-terminal-actions">
        <FocusButton context={context} focusId={rpgFocusId.restartRun} command={{ type: "restart-run" }} className="rpg-primary-action rpg-large-action" disabled={!context.onRestartRun}>New Run</FocusButton>
        <FocusButton context={context} focusId={rpgFocusId.exitRun} command={{ type: "exit-run" }} className="rpg-secondary-action rpg-large-action" disabled={!context.onExitRun}>Mode Select</FocusButton>
      </div>
    </section>
  );
}

function CompactCombatHud({ context }: { context: RpgRunUiContext }) {
  const { run } = context;
  const boss = currentBoss(run);
  const cleared = run.phase === "boss-cleared";
  return (
    <section className="rpg-compact-combat-hud" aria-live="polite">
      <div>
        <small>{run.phase === "challenge-active" ? "Hallway challenge" : cleared ? "Room cleared" : "RPG Roguelike"}</small>
        <strong>{run.phase === "challenge-active" ? run.currentChallenge?.objective.name : cleared ? "North gate open · cross it or confirm" : boss?.name}</strong>
      </div>
      {run.phase === "challenge-active" ? <ChallengeObjective run={run} compact /> : <RoutePips run={run} />}
    </section>
  );
}

/** Main-display overlay. Draft/reward/shop phases gate play; live phases render compact HUD only. */
export function RpgRunOverlay(props: RpgRunUiProps) {
  const context = createUiContext(props);
  const live = props.run.phase === "challenge-active" || props.run.phase === "boss-combat" || props.run.phase === "boss-cleared";
  const overlayRef = useRef<HTMLDivElement>(null);
  const previousFocusRef = useRef<HTMLElement | null>(null);

  useEffect(() => {
    if (live) return;
    previousFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    return () => {
      const previous = previousFocusRef.current;
      if (previous?.isConnected) previous.focus({ preventScroll: true });
      previousFocusRef.current = null;
    };
  }, [live]);

  useEffect(() => {
    if (live) return;
    const frame = requestAnimationFrame(() => {
      const selected = overlayRef.current?.querySelector<HTMLButtonElement>("[data-rpg-focus].is-controller-selected:not(:disabled)");
      selected?.focus({ preventScroll: true });
    });
    return () => cancelAnimationFrame(frame);
  }, [context.activeFocusId, live, props.run.phase]);

  const trapModalTab = (event: KeyboardEvent<HTMLDivElement>) => {
    if (live || event.key !== "Tab") return;
    const buttons = [...(overlayRef.current?.querySelectorAll<HTMLButtonElement>("button:not(:disabled)") ?? [])];
    if (!buttons.length) {
      event.preventDefault();
      overlayRef.current?.focus({ preventScroll: true });
      return;
    }
    const currentIndex = buttons.indexOf(document.activeElement as HTMLButtonElement);
    const nextIndex = event.shiftKey
      ? currentIndex <= 0 ? buttons.length - 1 : currentIndex - 1
      : currentIndex < 0 || currentIndex === buttons.length - 1 ? 0 : currentIndex + 1;
    event.preventDefault();
    buttons[nextIndex].focus({ preventScroll: true });
  };
  return (
    <div
      ref={overlayRef}
      className={`rpg-run-overlay ${live ? "is-live" : "is-gated"} ${props.className ?? ""}`.trim()}
      role={live ? "status" : "dialog"}
      aria-modal={live ? undefined : true}
      aria-label="RPG roguelike run"
      tabIndex={live ? undefined : -1}
      onKeyDown={trapModalTab}
    >
      {live ? <CompactCombatHud context={context} /> : (
        <>
          <RunHeader run={props.run} />
          <RoutePips run={props.run} />
          {props.run.phase === "party-draft" && <PartyDraft context={context} />}
          {props.run.phase === "spell-draft" && <SpellDraft context={context} />}
          {props.run.phase === "challenge-briefing" && <Briefing context={context} kind="challenge" />}
          {props.run.phase === "boss-briefing" && <Briefing context={context} kind="boss" />}
          {props.run.phase === "reward" && <Rewards context={context} />}
          {props.run.phase === "shop" && <Shop context={context} />}
          {(props.run.phase === "victory" || props.run.phase === "defeat") && <Terminal context={context} />}
        </>
      )}
    </div>
  );
}
