// components.jsx — shared: logos, icons, attack map, animated counters

function Logo({ height = 28 }) {
  return <img src="assets/logo-white.png" alt="WP Guardian" style={{ height, display: 'block' }} />;
}

function ShieldIcon({ size = 16, color = 'currentColor' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
    </svg>
  );
}

function CheckIcon({ size = 16, color = '#22c55e' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="20 6 9 17 4 12" />
    </svg>
  );
}

function ArrowIcon({ size = 14 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <line x1="5" y1="12" x2="19" y2="12" />
      <polyline points="12 5 19 12 12 19" />
    </svg>
  );
}

// Counts up to target whenever component appears in viewport
function CountUp({ to, duration = 1800, format = (n) => Math.round(n).toLocaleString('es') , suffix = '', className = '' }) {
  const [val, setVal] = React.useState(0);
  const ref = React.useRef(null);
  const started = React.useRef(false);

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const obs = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting && !started.current) {
          started.current = true;
          const start = performance.now();
          const tick = (now) => {
            const t = Math.min(1, (now - start) / duration);
            const eased = 1 - Math.pow(1 - t, 3);
            setVal(eased * to);
            if (t < 1) requestAnimationFrame(tick);
          };
          requestAnimationFrame(tick);
        }
      });
    }, { threshold: 0.4 });
    obs.observe(el);
    return () => obs.disconnect();
  }, [to, duration]);

  return <span ref={ref} className={className}>{format(val)}{suffix}</span>;
}

// Background world map shape (SVG silhouette + animated threat blips)
function AttackMap() {
  // A handful of "incidents" positioned roughly across continents
  const blips = React.useMemo(() => [
    { x: '18%', y: '38%' }, { x: '22%', y: '52%' }, { x: '12%', y: '46%' },
    { x: '32%', y: '34%' }, { x: '46%', y: '32%' }, { x: '54%', y: '38%' },
    { x: '64%', y: '36%' }, { x: '72%', y: '44%' }, { x: '78%', y: '52%' },
    { x: '82%', y: '36%' }, { x: '40%', y: '62%' }, { x: '60%', y: '60%' },
    { x: '28%', y: '64%' }, { x: '88%', y: '60%' }, { x: '14%', y: '60%' },
  ], []);

  return (
    <div className="attack-map" aria-hidden="true">
      <svg viewBox="0 0 1200 600" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" style={{ position: 'absolute', inset: 0, opacity: 0.35 }}>
        <defs>
          <pattern id="dots" width="10" height="10" patternUnits="userSpaceOnUse">
            <circle cx="2" cy="2" r="1" fill="rgba(30,156,255,0.35)" />
          </pattern>
          <mask id="continents">
            {/* Rough continental shapes — schematic, not geographic */}
            <rect width="1200" height="600" fill="black" />
            <g fill="white">
              {/* N. America */}
              <path d="M120 140 Q200 110 280 150 Q330 210 290 280 Q230 320 180 300 Q120 280 100 230 Z" />
              {/* S. America */}
              <path d="M240 320 Q290 310 310 380 Q300 460 260 500 Q220 510 210 450 Z" />
              {/* Europe */}
              <path d="M520 130 Q580 120 600 160 Q580 210 540 220 Q500 200 500 160 Z" />
              {/* Africa */}
              <path d="M560 240 Q620 240 640 320 Q620 410 570 440 Q520 410 520 330 Z" />
              {/* Asia */}
              <path d="M640 130 Q780 110 880 150 Q940 200 920 270 Q830 310 720 290 Q650 250 640 200 Z" />
              {/* SE Asia / Oceania */}
              <path d="M900 320 Q960 310 1000 350 Q1010 410 960 430 Q900 420 890 380 Z" />
              <path d="M940 460 Q990 450 1020 480 Q1010 520 970 520 Q930 510 930 480 Z" />
            </g>
          </mask>
        </defs>
        <rect width="1200" height="600" fill="url(#dots)" mask="url(#continents)" />
      </svg>
      {blips.map((b, i) => (
        <React.Fragment key={i}>
          <span className="threat-ring" style={{ left: b.x, top: b.y, animationDelay: `${(i * 0.27) % 2}s` }} />
          <span className="threat-dot" style={{ left: b.x, top: b.y, animationDelay: `${(i * 0.31) % 2}s` }} />
        </React.Fragment>
      ))}
    </div>
  );
}

// Streaming "security events" terminal
function ThreatTerminal({ height = 360 }) {
  const seedLines = React.useMemo(() => ([
    { kind: 'block', ip: '185.92.221.14', msg: 'admin brute-force blocked', loc: 'RU' },
    { kind: 'block', ip: '103.45.118.220', msg: 'wp-login.php → 412 attempts', loc: 'CN' },
    { kind: 'warn',  ip: '—',              msg: 'CVE-2026-2418 detected (Elementor < 3.8.1)' },
    { kind: 'ok',    ip: '—',              msg: 'backup snapshot saved → s3://wpg/2026-05-14' },
    { kind: 'info',  ip: '—',              msg: 'plugin scan complete · 14 outdated · 2 critical' },
    { kind: 'block', ip: '45.137.21.88',   msg: 'xmlrpc.php flood blocked', loc: 'NL' },
    { kind: 'ok',    ip: '—',              msg: 'WordPress core 6.9.3 → 6.9.4 ✓' },
    { kind: 'warn',  ip: '—',              msg: 'AIO Backup last run 13d ago — escalating' },
    { kind: 'block', ip: '94.232.41.7',    msg: 'malicious file upload rejected (php in /uploads)', loc: 'UA' },
    { kind: 'info',  ip: '—',              msg: 'Wordfence rules synced · 1,284 active' },
    { kind: 'block', ip: '198.235.24.12',  msg: 'SQLi attempt on ?s= parameter', loc: 'US' },
    { kind: 'ok',    ip: '—',              msg: 'PHP 8.4.19 OK · WP-CLI 2.12.0 OK' },
    { kind: 'warn',  ip: '—',              msg: 'theme "Newspaper" outdated · vendor pending' },
    { kind: 'block', ip: '77.83.36.220',   msg: 'enumeration probe on /author=1', loc: 'BG' },
  ]), []);

  const [lines, setLines] = React.useState(() => seedLines.slice(0, 9));

  React.useEffect(() => {
    let i = 9;
    const id = setInterval(() => {
      setLines(prev => {
        const next = [...prev, seedLines[i % seedLines.length]];
        i++;
        return next.slice(-9);
      });
    }, 1600);
    return () => clearInterval(id);
  }, [seedLines]);

  const now = () => {
    const d = new Date();
    return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`;
  };

  return (
    <div className="terminal">
      <div className="terminal-hd">
        <div className="frame-dots">
          <div className="frame-dot r" /><div className="frame-dot y" /><div className="frame-dot g" />
        </div>
        <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-muted)', flex: 1 }}>
          wpguardian · live security log
        </span>
        <span className="pill live"><span className="dot pulse" />LIVE</span>
      </div>
      <div className="terminal-body" style={{ height }}>
        {lines.map((l, i) => (
          <div className="term-line" key={`${i}-${l.msg}`}>
            <span className="term-ts">[{now()}]</span>
            <span className={`term-tag ${l.kind}`}>{l.kind === 'block' ? 'BLOCK' : l.kind === 'warn' ? 'ALERT' : l.kind === 'ok' ? 'OK' : 'INFO'}</span>
            {l.ip !== '—' && <span style={{ color: 'var(--text)' }}>{l.ip}</span>}
            {l.loc && <span style={{ color: 'var(--text-dim)' }}>·{l.loc}</span>}
            <span style={{ color: 'var(--text-muted)' }}>{l.msg}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { Logo, ShieldIcon, CheckIcon, ArrowIcon, CountUp, AttackMap, ThreatTerminal });
