/* global React, ZoneBackdrop */
/**
 * Combat V2 — Pantalla de loadout (selección de 3 habilidades por PJ antes del combate).
 *
 * Carga `AbilityIcon` desde `replay-adapter.jsx` si está presente en `window`.
 * Si también se carga `screens-combat.jsx`, el último script que asigne `window.LoadoutScreen` gana.
 *
 * Uso:
 *   <LoadoutScreen party={...} abilitiesMap={map} zoneName="..." onStartCombat={fn} onBack={fn} />
 */

const { useState, useMemo, useCallback, useEffect } = React;

const SLOT_KEYS = ['of', 'df', 'si'];
const SLOT_LABELS = { of: 'Ofensiva', df: 'Defensiva', si: 'Situacional' };

const FALLBACK_ZONE_BG = `linear-gradient(180deg, #0b0d14 0%, #141a28 50%, #0a0c12 100%)`;
const FALLBACK_ZONE_SIL = 'm 0 70 l 50 55 l 100 70 L 100 100 L 0 100 Z';

const CLASS_VISUAL = {
  Wizard: { glyph: '🔮', tint: '#6b8cff' },
  Mago: { glyph: '🔥', tint: '#c87840' },
  Druida: { glyph: '🌿', tint: '#5fb86b' },
  Guerrero: { glyph: '⚔', tint: '#c84860' },
  Mercenario: { glyph: '🛡', tint: '#a89870' },
  Paladin: { glyph: '✝', tint: '#e8d060' },
  'Shadow Knight': { glyph: '💀', tint: '#7080a0' },
  Clérigo: { glyph: '✨', tint: '#f0e8c8' },
  Shaman: { glyph: '🔆', tint: '#50a0c0' },
  Enchanter: { glyph: '💠', tint: '#a070e0' },
  Bard: { glyph: '🎵', tint: '#8a6dd9' },
  Rogue: { glyph: '🗡', tint: '#c8a860' },
  Hunter: { glyph: '🏹', tint: '#7ab060' },
  Monk: { glyph: '👊', tint: '#d0a060' },
  Necromancer: { glyph: '☠', tint: '#708868' },
};

function safeStr(v, fb = '') {
  if (v == null) return fb;
  return typeof v === 'string' ? v : String(v);
}

function memberIdOf(m) {
  if (!m || typeof m !== 'object') return '';
  return safeStr(m.id ?? m.memberId, '');
}

function memberClass(m) {
  if (!m || typeof m !== 'object') return '';
  return safeStr(m.class ?? m.cls, '');
}

function memberLevel(m) {
  if (!m || typeof m !== 'object') return 20;
  const lv = m.level ?? m.lvl;
  if (typeof lv === 'number' && lv >= 1 && lv <= 60) return lv;
  return 20;
}

function memberName(m) {
  return safeStr(m?.name, '—');
}

/** Todas las habilidades V2 de la clase en el slot (orden: nivel, id). */
function listAbilitiesForSlot(member, slot, abilitiesMap) {
  const cls = memberClass(member);
  const am = abilitiesMap instanceof Map ? abilitiesMap : new Map();
  const out = [];
  am.forEach((def) => {
    if (!def || typeof def !== 'object') return;
    if (safeStr(def.class) !== cls) return;
    if (def.category !== slot) return;
    out.push(def);
  });
  out.sort((a, b) => (a.level || 1) - (b.level || 1) || safeStr(a.id).localeCompare(safeStr(b.id)));
  return out;
}

function isUnlocked(def, level) {
  const req = typeof def.level === 'number' ? def.level : 1;
  return req <= level;
}

/**
 * Map<memberId, { of: string|null, df: string|null, si: string|null }>
 * Pre-selecciona la primera habilidad desbloqueada por slot (menor nivel, luego id).
 */
function buildInitialSelections(party, abilitiesMap) {
  const map = new Map();
  const list = Array.isArray(party) ? party : [];
  const am = abilitiesMap instanceof Map ? abilitiesMap : new Map();

  for (const member of list) {
    const mid = memberIdOf(member);
    if (!mid) continue;
    const lv = memberLevel(member);
    const sel = { of: null, df: null, si: null };
    for (const slot of SLOT_KEYS) {
      const all = listAbilitiesForSlot(member, slot, am);
      const unlocked = all.filter((d) => isUnlocked(d, lv));
      if (unlocked.length > 0) sel[slot] = safeStr(unlocked[0].id, null);
    }
    map.set(mid, sel);
  }
  return map;
}

function cloneSelectionsMap(src) {
  const n = new Map();
  if (!(src instanceof Map)) return n;
  src.forEach((v, k) => {
    n.set(k, { of: v.of ?? null, df: v.df ?? null, si: v.si ?? null });
  });
  return n;
}

function slotHasAnyUnlocked(member, slot, abilitiesMap) {
  const lv = memberLevel(member);
  return listAbilitiesForSlot(member, slot, abilitiesMap).some((d) => isUnlocked(d, lv));
}

/** true si se puede iniciar combate según reglas (slots con pool vacío omitidos). */
function computeCanStartCombat(party, abilitiesMap, selections) {
  const list = Array.isArray(party) ? party : [];
  const am = abilitiesMap instanceof Map ? abilitiesMap : new Map();
  if (list.length === 0) return false;

  for (const member of list) {
    const mid = memberIdOf(member);
    if (!mid) return false;
    const row = selections instanceof Map ? selections.get(mid) : null;
    const pick = row || { of: null, df: null, si: null };

    for (const slot of SLOT_KEYS) {
      if (!slotHasAnyUnlocked(member, slot, am)) continue;
      if (!pick[slot]) return false;
    }
  }
  return true;
}

function buildTooltip(def) {
  if (!def) return '';
  const desc = safeStr(def.description, '');
  const cd = typeof def.cd_ms === 'number' ? def.cd_ms / 1000 : 0;
  const cdLine = `CD: ${cd.toFixed(cd % 1 === 0 ? 0 : 1)} s`;
  return `${desc}\n\n${cdLine}`.trim();
}

function AbilityGlyphFallback({ def }) {
  const g = typeof window.abilityDefToUiAbility === 'function'
    ? window.abilityDefToUiAbility(def).glyph
    : '✦';
  return (
    <span style={{ fontSize: 22, lineHeight: 1 }}>{g}</span>
  );
}

function LoadoutAbilityCard({
  def,
  selected,
  locked,
  slotColorVar,
  onSelect,
  abilitiesMap,
}) {
  const id = def && def.id;
  const slotState = def
    ? {
        ability_id: id,
        cooldownRemainingMs: 0,
        cdMs: typeof def.cd_ms === 'number' ? def.cd_ms : 20000,
        xpEarned: 0,
      }
    : null;

  const AbilityIcon = typeof window.AbilityIcon === 'function' ? window.AbilityIcon : null;
  const title = buildTooltip(def);

  const baseStyle = {
    borderRadius: 10,
    padding: 8,
    minWidth: 72,
    maxWidth: 120,
    cursor: locked ? 'not-allowed' : 'pointer',
    opacity: locked ? 0.4 : 1,
    border: selected
      ? '2px solid var(--accent, #c8a860)'
      : '1px solid rgba(255,255,255,0.15)',
    background: selected ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.25)',
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    gap: 4,
    transition: 'border-color 0.15s, background 0.15s, opacity 0.15s',
    boxShadow: selected ? '0 0 0 1px var(--accent-glow, rgba(200,168,96,0.25))' : 'none',
  };

  return (
    <div
      role="button"
      tabIndex={locked ? -1 : 0}
      title={title}
      className="loadout-ability-card"
      style={baseStyle}
      onClick={() => { if (!locked && onSelect) onSelect(id); }}
      onKeyDown={(e) => {
        if (locked) return;
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onSelect && onSelect(id);
        }
      }}
      onMouseEnter={(e) => {
        if (!locked && !selected) e.currentTarget.style.background = 'rgba(255,255,255,0.06)';
      }}
      onMouseLeave={(e) => {
        if (!selected) e.currentTarget.style.background = 'rgba(0,0,0,0.25)';
        else e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
      }}
    >
      <div style={{ borderBottom: `2px solid ${slotColorVar}`, width: '100%', marginBottom: 2 }} />
      <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: 48 }}>
        {locked ? (
          <span style={{ fontSize: 26 }} title={title}>🔒</span>
        ) : AbilityIcon && slotState ? (
          <AbilityIcon slot={slotState} abilitiesMap={abilitiesMap instanceof Map ? abilitiesMap : new Map()} slotLabel="" />
        ) : (
          <AbilityGlyphFallback def={def} />
        )}
      </div>
      <span style={{ fontSize: 10, color: 'var(--ink-1, #c8c1ac)', textAlign: 'center', lineHeight: 1.2 }}>
        {locked ? `Nv. ${def.level}` : safeStr(def.id, '').replace(/^[^_]+_/, '').replace(/_/g, ' ').slice(0, 18)}
      </span>
    </div>
  );
}

function MemberLoadoutColumn({
  member,
  abilitiesMap,
  selections,
  onPick,
}) {
  const mid = memberIdOf(member);
  const cls = memberClass(member);
  const vis = CLASS_VISUAL[cls] || { glyph: '⚔', tint: '#8890a8' };
  const row = selections.get(mid) || { of: null, df: null, si: null };
  const lv = memberLevel(member);
  const img = member.portraitImg || member.portrait_img || null;
  const [imgErr, setImgErr] = useState(false);

  return (
    <div className="loadout-member-col" style={{
      flex: '1 1 220px',
      minWidth: 200,
      maxWidth: 320,
      padding: 14,
      borderRadius: 12,
      background: 'var(--bg-2, #10141d)',
      border: '1px solid rgba(255,255,255,0.08)',
      display: 'flex',
      flexDirection: 'column',
      gap: 12,
    }}>
      <div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
        <div
          style={{
            width: 56,
            height: 56,
            borderRadius: 10,
            overflow: 'hidden',
            flexShrink: 0,
            background: `radial-gradient(ellipse at center, ${vis.tint}33 0%, transparent 70%), var(--bg-1)`,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            fontSize: 28,
          }}
        >
          {img && !imgErr ? (
            <img
              src={img}
              alt=""
              style={{ width: '100%', height: '100%', objectFit: 'cover' }}
              onError={() => setImgErr(true)}
            />
          ) : (
            <span>{vis.glyph}</span>
          )}
        </div>
        <div>
          <div style={{ fontFamily: 'Cinzel, serif', fontSize: 16, color: 'var(--ink-0)' }}>{memberName(member)}</div>
          <div style={{ fontSize: 12, color: 'var(--ink-2)' }}>{cls} · Nv. {lv}</div>
        </div>
      </div>

      {SLOT_KEYS.map((slot) => {
        const defs = listAbilitiesForSlot(member, slot, abilitiesMap);
        const colorVar = slot === 'of' ? 'var(--color-of, #c0392b)' : slot === 'df' ? 'var(--color-df, #2980b9)' : 'var(--color-si, #27ae60)';
        return (
          <div key={slot} className="loadout-slot-block">
            <div style={{
              fontSize: 11,
              letterSpacing: '0.12em',
              textTransform: 'uppercase',
              color: colorVar,
              marginBottom: 6,
            }}>{SLOT_LABELS[slot]}</div>
            {defs.length === 0 ? (
              <div style={{ fontSize: 12, color: 'var(--ink-3)', fontStyle: 'italic' }}>Sin habilidades en datos</div>
            ) : (
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
                {defs.map((def) => {
                  const unlocked = isUnlocked(def, lv);
                  const selected = row[slot] === def.id;
                  return (
                    <LoadoutAbilityCard
                      key={def.id}
                      def={def}
                      selected={selected}
                      locked={!unlocked}
                      slotColorVar={colorVar}
                      abilitiesMap={abilitiesMap}
                      onSelect={(aid) => onPick(mid, slot, aid)}
                    />
                  );
                })}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

/**
 * @param {{
 *   party: object[],
 *   abilitiesMap: Map<string, object>,
 *   zoneName: string,
 *   onStartCombat: (loadout: { memberId: string, equippedAbilities: { of: string|null, df: string|null, si: string|null } }[]) => void,
 *   onBack: (() => void) | null | undefined,
 * }} props
 */
function LoadoutScreen({
  party,
  abilitiesMap,
  zoneName,
  onStartCombat,
  onBack,
}) {
  const partyList = Array.isArray(party) ? party : [];
  const am = abilitiesMap instanceof Map ? abilitiesMap : new Map();

  const [selections, setSelections] = useState(() => buildInitialSelections(partyList, am));

  const partySig = useMemo(
    () => partyList.map((m) => `${memberIdOf(m)}|${memberClass(m)}|${memberLevel(m)}`).join(','),
    [partyList],
  );

  useEffect(() => {
    setSelections(buildInitialSelections(partyList, am));
  }, [partySig, am.size]);

  const canStart = useMemo(
    () => computeCanStartCombat(partyList, am, selections),
    [partyList, am, selections],
  );

  const onPick = useCallback((memberId, slot, abilityId) => {
    setSelections((prev) => {
      const next = cloneSelectionsMap(prev);
      const cur = next.get(memberId) || { of: null, df: null, si: null };
      next.set(memberId, { ...cur, [slot]: abilityId });
      return next;
    });
  }, []);

  const handleCombat = useCallback(() => {
    if (!canStart || typeof onStartCombat !== 'function') return;
    const loadout = partyList.map((m) => {
      const mid = memberIdOf(m);
      const row = selections.get(mid) || { of: null, df: null, si: null };
      return {
        memberId: mid,
        equippedAbilities: {
          of: row.of ?? null,
          df: row.df ?? null,
          si: row.si ?? null,
        },
      };
    });
    onStartCombat(loadout);
  }, [canStart, onStartCombat, partyList, selections]);

  const zone = useMemo(() => ({
    id: 'loadout_zone',
    name: safeStr(zoneName, 'Zona'),
    tier: 'Loadout',
    bg: FALLBACK_ZONE_BG,
    silhouette: FALLBACK_ZONE_SIL,
    horizon: 62,
    mood: 'misty',
  }), [zoneName]);

  const ZB = typeof ZoneBackdrop === 'function' ? ZoneBackdrop : null;
  const emptyMap = am.size === 0;

  return (
    <div
      className="loadout-screen-v2"
      data-screen-label="Loadout V2"
      style={{
        position: 'absolute',
        inset: 0,
        display: 'flex',
        flexDirection: 'column',
        color: 'var(--ink-0, #efe9d7)',
        '--color-of': '#c0392b',
        '--color-df': '#2980b9',
        '--color-si': '#27ae60',
      }}
    >
      <style>{`
        .loadout-screen-v2 .loadout-head-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
        .loadout-screen-v2 .loadout-grid { display: flex; flex-wrap: wrap; gap: 16px; padding: 16px 20px 100px; z-index: 2; position: relative; justify-content: center; }
        .loadout-screen-v2 .loadout-foot { position: fixed; bottom: 0; left: 0; right: 0; padding: 16px 24px; background: linear-gradient(0deg, rgba(7,9,14,0.95), transparent); z-index: 10; display: flex; justify-content: center; gap: 16px; flex-wrap: wrap; align-items: center; }
      `}</style>

      {ZB ? <ZB zone={zone} /> : null}

      <div style={{ zIndex: 4, position: 'relative', padding: '20px 24px 0' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: 12 }}>
          <div>
            <div style={{ fontSize: 11, letterSpacing: '0.2em', color: 'var(--ink-2)', textTransform: 'uppercase' }}>Preparación · Combat V2</div>
            <h1 style={{ fontFamily: 'Cinzel, serif', fontSize: 26, margin: '6px 0 0', color: 'var(--ink-0)' }}>{safeStr(zoneName, 'Zona')}</h1>
            <p style={{ margin: '8px 0 0', fontSize: 13, color: 'var(--ink-2)', maxWidth: 560 }}>
              Elige una habilidad por categoría (of / df / si) para cada héroe. Las bloqueadas requieren nivel. Pasa el cursor para ver descripción y CD.
            </p>
          </div>
          <div className="loadout-head-actions">
            {typeof onBack === 'function' ? (
              <button type="button" className="btn btn-ghost" onClick={onBack}>← Volver</button>
            ) : null}
          </div>
        </div>
      </div>

      {emptyMap ? (
        <div style={{ zIndex: 4, padding: '12px 24px', color: 'var(--accent-soft, #a08840)' }}>
          Sin habilidades cargadas — importa <code>abilities.v2.json</code> en un Map antes de abrir esta pantalla.
        </div>
      ) : null}

      <div className="loadout-grid">
        {partyList.map((m, idx) => (
          <MemberLoadoutColumn
            key={memberIdOf(m) || `party-${idx}`}
            member={m}
            abilitiesMap={am}
            selections={selections}
            onPick={onPick}
          />
        ))}
      </div>

      <div className="loadout-foot">
        <button
          type="button"
          className="btn btn-accent btn-xl"
          disabled={!canStart}
          onClick={handleCombat}
          title={!canStart ? 'Completa los slots obligatorios (categorías con habilidades disponibles).' : ''}
        >
          ▶ Combatir
        </button>
      </div>
    </div>
  );
}

Object.assign(window, {
  LoadoutScreen,
  buildInitialSelections,
});
