// ViG Command Center — svs.info live sync (parser + fetch + 24h auto-refresh + panel)
const { useState: useStateSY, useEffect: useEffectSY, useMemo: useMemoSY } = React;

/* ---- normalize a power value to billions (B) ---- */
function svsToB(v) {
  if (v == null) return null;
  if (typeof v === 'number') return v > 1e6 ? v / 1e9 : v;
  const m = /([\d.,]+)\s*([BMK]?)/i.exec(String(v));
  if (!m) return null;
  let n = parseFloat(m[1].replace(/,/g, '')); if (isNaN(n)) return null;
  const u = (m[2] || '').toUpperCase();
  if (u === 'M') n /= 1000; else if (u === 'K') n /= 1e6; else if (!u && n > 1e6) n /= 1e9;
  return n;
}

/* ---- try to read a full roster embedded as JSON (Next.js __NEXT_DATA__ etc.) ---- */
function svsFindMembers(node, depth) {
  if (!node || depth > 8) return null;
  if (Array.isArray(node)) {
    const ok = node.filter(x => x && typeof x === 'object');
    const named = ok.filter(x => Object.keys(x).some(k => /name|nick|player|user/i.test(k)) && Object.keys(x).some(k => /power|might|gp/i.test(k)));
    if (named.length >= 10) return named;
    for (const x of node) { const r = svsFindMembers(x, depth + 1); if (r) return r; }
    return null;
  }
  if (typeof node === 'object') {
    for (const k of Object.keys(node)) { const r = svsFindMembers(node[k], depth + 1); if (r) return r; }
  }
  return null;
}
function extractEmbeddedRoster(doc) {
  for (const s of [...doc.querySelectorAll('script')]) {
    const t = s.textContent || '';
    if (t.length < 200 || !/power|might/i.test(t)) continue;
    let data = null;
    try { data = JSON.parse(t); } catch (e) {
      const m = t.match(/\{[\s\S]*\}/); if (m) { try { data = JSON.parse(m[0]); } catch (e2) {} }
    }
    if (!data) continue;
    const members = svsFindMembers(data, 0);
    if (members && members.length) {
      const players = members.map(m => {
        const nameKey = Object.keys(m).find(k => /name|nick|player|user/i.test(k));
        const powerKey = Object.keys(m).find(k => /power|might|gp/i.test(k));
        const keepKey = Object.keys(m).find(k => /keep|castle|stronghold/i.test(k));
        const monKey = Object.keys(m).find(k => /monarch|level|lord/i.test(k));
        const power = svsToB(m[powerKey]);
        let keep = null;
        if (keepKey != null) { const kv = String(m[keepKey]); keep = /^\d+$/.test(kv) ? 'K' + kv : (/(K\d{2})/i.exec(kv) || [])[1] || null; }
        return { name: String(m[nameKey] || '').trim(), keep, power, monarch: monKey ? +m[monKey] : null };
      }).filter(p => p.name && p.power != null && p.power > 0.05 && p.power < 5000);
      if (players.length >= 10) return players;
    }
  }
  return null;
}

/* ---- parse a svs.info alliance page (server-rendered HTML) ---- */
function parseSvsHtml(html) {
  const doc = new DOMParser().parseFromString(html, 'text/html');
  const text = (doc.body && doc.body.textContent) || '';
  let power = null;
  const pm = /Power[:\s]*([\d.,]+)\s*B/i.exec(text);
  if (pm) power = parseFloat(pm[1].replace(/,/g, ''));
  let memberCount = null;
  const mm = /Members[:\s]*([\d,]+)\s*\/\s*([\d,]+)/i.exec(text);
  if (mm) memberCount = parseInt(mm[1].replace(/,/g, ''));

  // 1) preferred: embedded full roster
  const embedded = extractEmbeddedRoster(doc);
  if (embedded) return { power, memberCount, players: embedded, source: 'embedded' };

  // 2) fallback: scrape the rendered table (typically top 15 only)
  const players = [];
  const seen = new Set();
  [...doc.querySelectorAll('table tr')].forEach(tr => {
    const cells = [...tr.children].map(c => c.textContent.trim());
    if (cells.length < 3) return;
    const pIdx = cells.findIndex(c => /^[\d.,]+\s*[BMK]?$/i.test(c) && /B/i.test(c));
    if (pIdx < 1) return;
    const powerVal = parseFloat(cells[pIdx].replace(/[^\d.]/g, ''));
    if (!powerVal) return;
    const nameCell = cells[pIdx - 1];
    const km = /K\s?(\d{2})\s*$/.exec(nameCell);
    const keep = km ? ('K' + km[1]) : null;
    const name = nameCell.replace(/K\s?\d{2}\s*$/, '').trim();
    const monarch = cells[pIdx + 1] && /^\d+$/.test(cells[pIdx + 1]) ? parseInt(cells[pIdx + 1]) : null;
    if (!name || seen.has(name.toLowerCase())) return;
    seen.add(name.toLowerCase());
    players.push({ name, keep, power: powerVal, monarch });
  });
  return { power, memberCount, players, source: 'table' };
}

/* ---- run a full sync across all configured alliances ---- */
async function runSvsSync(cfg, applySync, onProgress) {
  const status = {};
  const out = [];
  for (const [id, url] of Object.entries(cfg.sources || {})) {
    if (!url) { status[id] = { ok: false, err: 'no URL' }; continue; }
    onProgress && onProgress({ ...status, [id]: { loading: true } });
    try {
      const body = await window.svsFetchText(url, cfg.proxy || null, 300);
      // feed could be JSON (a relay/Worker) or HTML (proxy of svs.info)
      let parsed;
      const trimmed = body.trim();
      if (trimmed[0] === '{' || trimmed[0] === '[') {
        const j = JSON.parse(trimmed);
        const node = Array.isArray(j) ? j.find(x => (x.id || '').toUpperCase() === id) : (j[id] || j);
        parsed = { power: node.power, players: node.players || node.members || [] };
      } else {
        parsed = parseSvsHtml(body);
      }
      if (!parsed.players.length) throw new Error('0 rows parsed');
      out.push({ id, power: parsed.power, memberCount: parsed.memberCount, players: parsed.players });
      status[id] = { ok: true, n: parsed.players.length, power: parsed.power, total: parsed.memberCount, source: parsed.source };
    } catch (e) {
      status[id] = { ok: false, err: (e && e.message) || 'failed' };
    }
    onProgress && onProgress({ ...status });
  }
  if (out.length) applySync(out);
  return { status, applied: out.length };
}

/* ---- auto-refresh once per 24h on load ---- */
function useAutoSync() {
  const { data, syncCfg, applySync } = useAlliance();
  useEffectSY(() => {
    if (!syncCfg || !syncCfg.auto) return;
    const last = (data.meta && data.meta.lastSync) || 0;
    if (Date.now() - last > 24 * 3600 * 1000) {
      runSvsSync(syncCfg, applySync, null).catch(() => {});
    }
  }, []); // once on mount
}

function timeAgo(ts) {
  if (!ts) return 'never';
  const s = Math.floor((Date.now() - ts) / 1000);
  if (s < 60) return 'just now';
  if (s < 3600) return Math.floor(s / 60) + 'm ago';
  if (s < 86400) return Math.floor(s / 3600) + 'h ago';
  return Math.floor(s / 86400) + 'd ago';
}

/* ---- live status strip (shown at top of War Room) ---- */
function LiveSyncBar({ onOpen }) {
  const { data, syncCfg } = useAlliance();
  const live = data.meta && data.meta.live;
  const last = data.meta && data.meta.lastSync;
  const nextIn = last ? Math.max(0, 24 - Math.floor((Date.now() - last) / 3600000)) : null;
  return (
    <div className="wr-panel relative mb-4 flex flex-wrap items-center gap-x-4 gap-y-2 overflow-hidden px-4 py-2.5">
      <div className="wr-scan"></div>
      <div className="relative flex items-center gap-2">
        <span className="relative flex h-2 w-2">
          {live && <span className="absolute inline-flex h-full w-full animate-ping rounded-full opacity-70" style={{ background: '#34d399' }}></span>}
          <span className="relative inline-flex h-2 w-2 rounded-full" style={{ background: live ? '#34d399' : '#565b6e' }}></span>
        </span>
        <span className="text-[12px] font-semibold uppercase tracking-[0.12em]" style={{ color: live ? '#34d399' : 'var(--wr-faint)' }}>{live ? 'Live data' : 'Snapshot data'}</span>
      </div>
      <div className="relative text-[12px]" style={{ color: 'var(--wr-dim)' }}>
        {live ? <>Synced <b style={{ color: '#cfd2dc' }}>{timeAgo(last)}</b> from the live source{nextIn != null && <> · auto-refresh in ~{nextIn}h</>}</>
          : <>Showing the imported snapshot · connect a live source for live updates</>}
      </div>
      <button onClick={onOpen} className="relative ml-auto inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-[12.5px] font-semibold transition-all hover:brightness-110" style={{ background: 'linear-gradient(180deg,#ff7d1a,#FF6B00)', color: '#fff', boxShadow: '0 6px 18px -8px rgba(255,107,0,.7)' }}>
        <Icon name="bolt" size={14} />Live Sync
      </button>
    </div>
  );
}

/* ---- the sync configuration + run modal ---- */
function SyncPanel({ onClose }) {
  const { data, syncCfg, setSyncCfg, applySync } = useAlliance();
  const [status, setStatus] = useStateSY(null);
  const [busy, setBusy] = useStateSY(false);
  const [cfg, setCfg] = useStateSY(syncCfg);

  const save = (patch) => { const n = { ...cfg, ...patch }; setCfg(n); setSyncCfg(patch); };
  const setSource = (id, url) => { const sources = { ...cfg.sources, [id]: url }; save({ sources }); };

  const sync = async () => {
    setBusy(true); setStatus(null);
    const r = await runSvsSync(cfg, applySync, (s) => setStatus({ ...s }));
    setStatus(r.status); setBusy(false);
  };

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <div onClick={onClose} className="absolute inset-0" style={{ background: 'rgba(5,6,9,.72)', backdropFilter: 'blur(3px)' }}></div>
      <div className="wr-panel relative w-full max-w-xl overflow-hidden" style={{ maxHeight: '88vh', overflowY: 'auto' }}>
        <div className="wr-scan"></div>
        <div className="relative flex items-center justify-between border-b px-5 py-3.5" style={{ borderColor: 'var(--wr-line)' }}>
          <div>
            <h3 className="text-[14px] font-bold uppercase tracking-[0.12em]" style={{ color: '#e7e9ef' }}>Live Sync</h3>
            <p className="mt-0.5 text-[11.5px]" style={{ color: 'var(--wr-faint)' }}>Pull current rosters & power for all three alliances</p>
          </div>
          <WrBtn size="sm" onClick={onClose}><Icon name="x" size={15} /></WrBtn>
        </div>

        <div className="relative space-y-4 p-5">
          <button onClick={sync} disabled={busy} className="flex w-full items-center justify-center gap-2 rounded-lg py-3 text-[14px] font-bold transition-all hover:brightness-110 disabled:opacity-60" style={{ background: 'linear-gradient(180deg,#ff7d1a,#FF6B00)', color: '#fff', boxShadow: '0 8px 22px -10px rgba(255,107,0,.7)' }}>
            <Icon name={busy ? 'target' : 'bolt'} size={17} className={busy ? 'animate-spin' : ''} />{busy ? 'Syncing…' : 'Sync now'}
          </button>

          {status && (
            <div className="space-y-1.5">
              {Object.entries(status).map(([id, s]) => (
                <div key={id} className="flex items-center gap-2.5 rounded-lg px-3 py-2 wr-chip">
                  <WrTag id={id} />
                  {s.loading ? <span className="text-[12px]" style={{ color: 'var(--wr-dim)' }}>fetching…</span>
                    : s.ok ? <span className="flex items-center gap-1.5 text-[12px]" style={{ color: '#34d399' }}><Icon name="check" size={13} />{s.n} members · {s.power ? s.power.toFixed(1) + 'B' : '—'}</span>
                      : <span className="flex items-center gap-1.5 text-[12px]" style={{ color: '#fb7185' }}><Icon name="x" size={13} />{s.err}</span>}
                </div>
              ))}
              {Object.values(status).every(s => !s.ok && !s.loading) && (
                <p className="rounded-lg px-3 py-2 text-[11.5px]" style={{ background: 'rgba(251,113,133,.08)', color: '#fda4af' }}>
                  Direct fetch was blocked or the relay is down. Set a different relay below, deploy the included Worker for a reliable feed, or use Import/Export to paste data manually.
                </p>
              )}
            </div>
          )}

          <div className="rounded-xl border p-3.5" style={{ borderColor: 'var(--wr-line)' }}>
            <label className="flex cursor-pointer items-center justify-between">
              <div>
                <div className="text-[13px] font-semibold" style={{ color: '#e7e9ef' }}>Auto-refresh every 24h</div>
                <div className="text-[11.5px]" style={{ color: 'var(--wr-faint)' }}>Re-syncs on load if a day has passed</div>
              </div>
              <button onClick={() => save({ auto: !cfg.auto })} className="relative h-5 w-9 shrink-0 rounded-full transition-colors" style={{ background: cfg.auto ? '#FF6B00' : '#2c2c31' }}>
                <span className="absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all" style={{ left: cfg.auto ? 16 : 2 }}></span>
              </button>
            </label>
          </div>

          <div>
            <div className="mb-1.5 text-[11px] font-semibold uppercase tracking-wide" style={{ color: 'var(--wr-faint)' }}>Relay / feed prefix</div>
            <input value={cfg.proxy} onChange={e => save({ proxy: e.target.value })} placeholder="https://your-worker.workers.dev/?url=  (leave blank for direct)" className="w-full rounded-lg px-3 py-2 text-[12px] wr-input wr-mono" />
            <p className="mt-1 text-[11px]" style={{ color: 'var(--wr-faint)' }}>The feed URL is appended (encoded). Point this at your deployed Worker for the reliable path, or a public relay.</p>
          </div>

          <div className="space-y-2">
            <div className="text-[11px] font-semibold uppercase tracking-wide" style={{ color: 'var(--wr-faint)' }}>Feed URLs</div>
            {['VIG', 'RSP', 'NEF'].map(id => (
              <div key={id} className="flex items-center gap-2">
                <WrTag id={id} />
                <input value={cfg.sources[id] || ''} onChange={e => setSource(id, e.target.value)} className="flex-1 rounded-lg px-3 py-1.5 text-[12px] wr-input wr-mono" />
              </div>
            ))}
          </div>

          <p className="text-[11px]" style={{ color: 'var(--wr-faint)' }}>
            Synced data overwrites the live view (deltas are computed against the previous sync). Your manual edits and the original snapshot can be restored anytime from Import / Export → Reset.
          </p>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { parseSvsHtml, runSvsSync, useAutoSync, LiveSyncBar, SyncPanel, syncTimeAgo: timeAgo });
