/* ============================================================
   FLOWSHIELD — shared UI components (JSX / babel)
   ============================================================ */
const { useState, useEffect, useRef, useMemo } = React;

/* Icon */
function Icon({ name, size = 18, style, className }) {
  const inner = window.ICONS[name] || '';
  return (
    <svg className={className} width={size} height={size} viewBox="0 0 24 24"
      fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"
      style={style} dangerouslySetInnerHTML={{ __html: inner }} />
  );
}

/* Brand logo: animated hex shield + flow wave */
function Logo({ size = 34, withText = true, glow = true }) {
  return (
    <div style={{ display:'flex', alignItems:'center', gap:11 }}>
      <div style={{ position:'relative', width:size, height:size, filter: glow ? 'drop-shadow(0 0 9px var(--fs-glow))' : 'none' }}>
        <svg width={size} height={size} viewBox="0 0 48 48" fill="none">
          <defs>
            <linearGradient id="fsg" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" stopColor="#00F5C0"/><stop offset="1" stopColor="#29E0FF"/>
            </linearGradient>
          </defs>
          <path d="M24 3l17 9.5v15L24 45 7 27.5v-15L24 3z" fill="#08110F" stroke="url(#fsg)" strokeWidth="1.6"/>
          <path className="fs-wave" d="M14 27c3-6 5 4 8-1s4-7 7-2 5-1 5-1" stroke="url(#fsg)" strokeWidth="2.2" fill="none" strokeLinecap="round"/>
          <circle cx="24" cy="15" r="1.8" fill="#00F5C0"/>
        </svg>
      </div>
      {withText && (
        <div style={{ lineHeight:1 }}>
          <div className="disp" style={{ fontWeight:700, fontSize:17, color:'var(--txt-hi)', letterSpacing:'.02em' }}>
            Flow<span style={{ color:'var(--fs)' }}>Shield</span>
          </div>
          <div className="mono" style={{ fontSize:8.5, letterSpacing:'.34em', color:'var(--txt-mute)', marginTop:3 }}>SEC OPS PLATFORM</div>
        </div>
      )}
      <style>{`.fs-wave{stroke-dasharray:60;stroke-dashoffset:60;animation:fswave 2.4s ease forwards .2s}@keyframes fswave{to{stroke-dashoffset:0}}`}</style>
    </div>
  );
}

/* Button */
function Btn({ children, variant = 'default', size, icon, iconR, onClick, style, className = '', glitch, type }) {
  const cls = ['btn'];
  if (variant === 'primary') cls.push('btn-primary');
  if (variant === 'solid') cls.push('btn-solid');
  if (variant === 'ghost') cls.push('btn-ghost');
  if (size === 'sm') cls.push('btn-sm');
  if (size === 'lg') cls.push('btn-lg');
  if (size === 'block') cls.push('btn-block');
  return (
    <button type={type||'button'} className={cls.join(' ') + ' ' + className} onClick={onClick} style={style}>
      {icon && <Icon name={icon} size={16} />}
      {glitch ? <span className="glitch" data-text={children}>{children}</span> : children}
      {iconR && <Icon name={iconR} size={16} />}
    </button>
  );
}

/* Severity badge */
function Sev({ level, pulse, count }) {
  return (
    <span className={'badge ' + window.sevClass(level)}>
      <span className={'dot ' + (pulse ? 'pulse' : '')} style={{ background:'currentColor' }} />
      {window.sevLabel(level)}{count != null ? ' · ' + count : ''}
    </span>
  );
}

/* Status badge */
function StatusBadge({ status }) {
  const map = {
    open:    { c:'sev-high', t:'Abierto' },
    triage:  { c:'sev-low',  t:'En triaje' },
    fixed:   { c:'sev-ok',   t:'Resuelto' },
    running: { c:'sev-low',  t:'En curso', pulse:true },
    done:    { c:'sev-ok',   t:'Completado' },
    pass:    { c:'sev-ok',   t:'PASS' },
    fail:    { c:'sev-crit', t:'FAIL' },
  };
  const m = map[status] || { c:'sev-info', t:status };
  return (
    <span className={'badge ' + m.c}>
      <span className={'dot ' + (m.pulse ? 'pulse' : '')} style={{ background:'currentColor' }} />{m.t}
    </span>
  );
}

/* Radial gauge */
function Gauge({ value, size = 116, label, sub, color = 'var(--acc)', thick = 9 }) {
  const r = (size - thick) / 2;
  const c = 2 * Math.PI * r;
  const [v, setV] = useState(0);
  useEffect(() => { const t = setTimeout(() => setV(value), 120); return () => clearTimeout(t); }, [value]);
  return (
    <div style={{ position:'relative', width:size, height:size }}>
      <svg width={size} height={size} style={{ transform:'rotate(-90deg)', overflow:'visible' }}>
        <circle cx={size/2} cy={size/2} r={r} stroke="var(--line)" strokeWidth={thick} fill="none" />
        <circle cx={size/2} cy={size/2} r={r} stroke={color} strokeWidth={thick} fill="none"
          strokeLinecap="round" strokeDasharray={c} strokeDashoffset={c - (c * v) / 100}
          style={{ transition:'stroke-dashoffset 1s cubic-bezier(.2,.8,.2,1)', filter:`drop-shadow(0 0 6px ${color})` }} />
      </svg>
      <div style={{ position:'absolute', inset:0, display:'grid', placeItems:'center', textAlign:'center' }}>
        <div>
          <div className="disp" style={{ fontSize:size*0.26, fontWeight:700, color:'var(--txt-hi)', lineHeight:1 }}>{label != null ? label : v + '%'}</div>
          {sub && <div className="mono" style={{ fontSize:9.5, color:'var(--txt-mute)', marginTop:4, letterSpacing:'.1em' }}>{sub}</div>}
        </div>
      </div>
    </div>
  );
}

/* Page header */
function PageHeader({ title, sub, crumb, right }) {
  return (
    <div style={{ display:'flex', alignItems:'flex-end', justifyContent:'space-between', gap:20, marginBottom:26, flexWrap:'wrap' }}>
      <div>
        {crumb && <div className="mono" style={{ fontSize:11, color:'var(--txt-mute)', letterSpacing:'.1em', marginBottom:10 }}>{crumb}</div>}
        <h1 className="disp" style={{ fontSize:30, fontWeight:700, color:'var(--txt-hi)', letterSpacing:'-.01em' }}>{title}</h1>
        {sub && <p style={{ fontSize:13.5, color:'var(--txt-dim)', marginTop:8, maxWidth:640, lineHeight:1.6 }}>{sub}</p>}
      </div>
      {right && <div style={{ display:'flex', gap:10 }}>{right}</div>}
    </div>
  );
}

/* Tool card with reticle + cursor-follow glow */
/* Re-render on plan change. */
function usePlan() {
  const [, bump] = useState(0);
  useEffect(() => (window.fsPlan ? window.fsPlan.sub(() => bump(n => n + 1)) : undefined), []);
  return window.fsPlan;
}

function ToolCard({ tool, onClick }) {
  const ref = useRef(null);
  usePlan();   // re-evaluar el bloqueo al cambiar de plan
  const eng = window.redteamEngine ? window.redteamEngine(tool.id) : null;
  const planned = !!(eng && eng.status === 'planned');
  const tierLocked = !planned && window.fsToolAllowed && !window.fsToolAllowed(tool.id);
  const locked = planned || tierLocked;
  const note = planned
    ? 'PREPARANDO MÓDULO · PRÓXIMAMENTE'
    : (tierLocked ? ('PLAN ' + (window.fsPlanForTier ? window.fsPlanForTier(window.fsToolTier(tool.id)).toUpperCase() : 'SUPERIOR') + ' · MEJORA TU PLAN') : '');
  const onMove = (e) => {
    if (locked) return;
    const el = ref.current; if (!el) return;
    const b = el.getBoundingClientRect();
    el.style.setProperty('--mx', ((e.clientX - b.left) / b.width) * 100 + '%');
    el.style.setProperty('--my', ((e.clientY - b.top) / b.height) * 100 + '%');
  };
  return (
    <div ref={ref} className={'tool-card theme-' + (tool.color || 'fs')} onMouseMove={onMove}
      onClick={locked ? undefined : onClick}
      style={locked ? { filter:'grayscale(.85)', opacity:.62, cursor:'not-allowed' } : undefined}
      title={planned ? 'Módulo en preparación · próximamente' : (tierLocked ? 'No incluido en tu plan' : undefined)}>
      <span className="bracket tl" /><span className="bracket tr" /><span className="bracket bl" /><span className="bracket br" />
      <div className="tool-ico"><Icon name={tool.icon} size={24} /></div>
      <div style={{ position:'relative', zIndex:1, flex:1 }}>
        <div className="tool-title" style={{ display:'flex', alignItems:'center', gap:8 }}>
          {tool.name}
          {locked && <Icon name="lock" size={13} style={{ color:'var(--txt-mute)', flexShrink:0 }} />}
        </div>
        <div className="tool-desc">{tool.desc}</div>
        {locked && (
          <div className="mono" style={{ fontSize:10, color:'var(--txt-mute)', marginTop:9, letterSpacing:'.06em', display:'flex', alignItems:'center', gap:6 }}>
            <Icon name="lock" size={11} /> {note}
          </div>
        )}
      </div>
    </div>
  );
}

/* Pantalla mostrada cuando se entra a un módulo no incluido en el plan. */
function PlanGate({ module, onNav }) {
  const plan = usePlan();
  const need = window.fsModuleTier ? window.fsModuleTier(module) : 1;
  const needName = window.fsPlanForTier ? window.fsPlanForTier(need) : 'superior';
  const titles = { blueteam:'Blue Team · Hardening', normativa:'Normativa · Cumplimiento' };
  return (
    <div className="enter">
      <PageHeader title={titles[module] || 'Módulo restringido'} crumb="ACCESO SEGÚN PLAN" />
      <div className="panel" style={{ padding:'42px 30px', textAlign:'center', maxWidth:560, margin:'24px auto' }}>
        <div style={{ width:66, height:66, borderRadius:16, display:'grid', placeItems:'center', margin:'0 auto 18px', background:'var(--acc-soft)', color:'var(--acc)', border:'1px solid color-mix(in srgb,var(--acc) 35%,transparent)' }}>
          <Icon name="lock" size={30} />
        </div>
        <div className="disp" style={{ fontSize:20, fontWeight:700, color:'var(--txt-hi)', marginBottom:8 }}>Módulo no incluido en tu plan</div>
        <div style={{ fontSize:13.5, color:'var(--txt-dim)', lineHeight:1.6, marginBottom:22 }}>
          Tu plan actual es <strong style={{ color:'var(--txt-hi)' }}>{plan ? plan.name() : '—'}</strong>. Este módulo requiere el plan <strong style={{ color:'var(--acc)' }}>{needName}</strong> o superior.
        </div>
        <Btn variant="primary" icon="rocket" onClick={()=> onNav && onNav('settings')}>Ver planes y cambiar</Btn>
      </div>
    </div>
  );
}

/* Stat tile */
function Stat({ label, value, icon, accent = 'var(--acc)', sub, delta }) {
  return (
    <div className="panel" style={{ padding:18, position:'relative', overflow:'hidden' }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start' }}>
        <div>
          <div className="mono upper" style={{ fontSize:10.5, color:'var(--txt-mute)', marginBottom:12 }}>{label}</div>
          <div className="disp" style={{ fontSize:34, fontWeight:700, color:'var(--txt-hi)', lineHeight:1 }}>{value}</div>
          {sub && <div style={{ fontSize:11.5, color:'var(--txt-dim)', marginTop:8 }}>{sub}</div>}
        </div>
        {icon && (
          <div style={{ width:38, height:38, borderRadius:9, display:'grid', placeItems:'center', color:accent, background:'color-mix(in srgb,'+accent+' 12%,transparent)', border:'1px solid color-mix(in srgb,'+accent+' 30%,transparent)' }}>
            <Icon name={icon} size={20} />
          </div>
        )}
      </div>
      {delta != null && (
        <div className="mono" style={{ fontSize:11, marginTop:10, color: delta >= 0 ? 'var(--ok)' : 'var(--red)' }}>
          {delta >= 0 ? '▲' : '▼'} {Math.abs(delta)} últimos 7d
        </div>
      )}
      <div style={{ position:'absolute', right:-20, bottom:-20, width:90, height:90, borderRadius:'50%', background:'color-mix(in srgb,'+accent+' 7%,transparent)', filter:'blur(8px)' }} />
    </div>
  );
}

/* Empty state */
function Empty({ icon, title, sub, action }) {
  return (
    <div style={{ display:'grid', placeItems:'center', padding:'70px 20px', textAlign:'center' }}>
      <div style={{ width:64, height:64, borderRadius:16, display:'grid', placeItems:'center', color:'var(--txt-mute)', background:'var(--bg-panel-2)', border:'1px solid var(--line)', marginBottom:18 }}>
        <Icon name={icon} size={30} />
      </div>
      <div className="disp" style={{ fontSize:17, color:'var(--txt-hi)', marginBottom:8 }}>{title}</div>
      {sub && <div style={{ fontSize:13, color:'var(--txt-dim)', maxWidth:380, lineHeight:1.6, marginBottom:18 }}>{sub}</div>}
      {action}
    </div>
  );
}

/* Donut for severity breakdown */
function SevDonut({ data, size = 130 }) {
  const order = ['crit','high','med','low'];
  const colors = { crit:'var(--sev-crit)', high:'var(--sev-high)', med:'var(--sev-med)', low:'var(--sev-low)' };
  const total = order.reduce((s,k)=>s+(data[k]||0),0) || 1;
  const thick = 14, r = (size-thick)/2, c = 2*Math.PI*r;
  let off = 0;
  return (
    <div style={{ position:'relative', width:size, height:size }}>
      <svg width={size} height={size} style={{ transform:'rotate(-90deg)', overflow:'visible' }}>
        <circle cx={size/2} cy={size/2} r={r} stroke="var(--line)" strokeWidth={thick} fill="none" />
        {order.map(k => {
          const val = data[k]||0; if (!val) return null;
          const len = (val/total)*c;
          const seg = <circle key={k} cx={size/2} cy={size/2} r={r} stroke={colors[k]} strokeWidth={thick} fill="none"
            strokeDasharray={`${len} ${c-len}`} strokeDashoffset={-off} style={{ filter:`drop-shadow(0 0 4px ${colors[k]})` }} />;
          off += len; return seg;
        })}
      </svg>
      <div style={{ position:'absolute', inset:0, display:'grid', placeItems:'center' }}>
        <div style={{ textAlign:'center' }}>
          <div className="disp" style={{ fontSize:28, fontWeight:700, color:'var(--txt-hi)', lineHeight:1 }}>{total}</div>
          <div className="mono" style={{ fontSize:9, color:'var(--txt-mute)', letterSpacing:'.1em', marginTop:3 }}>HALLAZGOS</div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { Icon, Logo, Btn, Sev, StatusBadge, Gauge, PageHeader, ToolCard, Stat, Empty, SevDonut, PlanGate, usePlan });
