// prior-art.jsx — التدقيق الاستباقي (محرك بحث حالة التقنية) + الربط بسلسلة الكتل
// Local TF-IDF cosine audit against a reference corpus, live patent-search links, and an internal hash chain.

const PRIOR_ART_DB = [
  { id: 'US-2023-019283', title: 'Smart Barcode System for Clinical Packaging',
    abstract: 'A system using digital barcodes on medical packages to display black box warnings automatically.' },
  { id: 'EP-2024-991204', title: 'Decentralized Intellectual Property Timestamping',
    abstract: 'Method for registering patents on public ledgers using sha256 hashing without semantic checking.' },
  { id: 'WO-2025-001298', title: 'Cellular Mapping and Dynamic High Dimensional Visualization',
    abstract: 'Interactive framework for mapping high dimensional cell metrics onto two dimensional dynamic canvas.' },
  { id: 'US-2022-448120', title: 'Predictive Triage Engine for Emergency Departments',
    abstract: 'Machine learning model predicting patient acuity at triage to reduce emergency waiting time and overcrowding.' },
  { id: 'WO-2024-556301', title: 'Medication Adherence Reminder Platform',
    abstract: 'Mobile platform sending personalized reminders and refill alerts to chronic disease patients with pharmacist follow up.' },
  { id: 'EP-2023-771902', title: 'Remote Monitoring of Chronic Disease Vital Signs',
    abstract: 'Connected devices streaming vital signs from home to a clinical dashboard with automated deterioration alerts.' },
];

const PATENT_SEARCH_ENGINES = [
  { id: 'gp', name: { ar: 'Google Patents', en: 'Google Patents' }, url: q => 'https://patents.google.com/?q=' + encodeURIComponent(q) },
  { id: 'wipo', name: { ar: 'WIPO Patentscope', en: 'WIPO Patentscope' }, url: q => 'https://patentscope.wipo.int/search/en/result.jsf?query=' + encodeURIComponent(q) },
  { id: 'epo', name: { ar: 'Espacenet (EPO)', en: 'Espacenet (EPO)' }, url: q => 'https://worldwide.espacenet.com/patent/search?q=' + encodeURIComponent(q) },
  { id: 'lens', name: { ar: 'Lens.org', en: 'Lens.org' }, url: q => 'https://www.lens.org/lens/search/patent/list?q=' + encodeURIComponent(q) },
  { id: 'scholar', name: { ar: 'Google Scholar', en: 'Google Scholar' }, url: q => 'https://scholar.google.com/scholar?q=' + encodeURIComponent(q) },
];

/* ---------- TF-IDF + cosine similarity (port of the audit engine) ---------- */
const STOP = new Set('the a an and or of for to in on with by is are be as at from using use system method device إلى من في على عن هذا هذه التي الذي مع نظام طريقة جهاز'.split(/\s+/));
function tokens(s) {
  return String(s || '').toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, ' ').split(/\s+/).filter(w => w.length > 2 && !STOP.has(w));
}
function tfidfVectors(docs) {
  const bags = docs.map(tokens);
  const df = new Map();
  bags.forEach(b => new Set(b).forEach(w => df.set(w, (df.get(w) || 0) + 1)));
  const vocab = [...df.keys()];
  const N = docs.length;
  return bags.map(b => {
    const tf = new Map();
    b.forEach(w => tf.set(w, (tf.get(w) || 0) + 1));
    return vocab.map(w => (tf.get(w) ? (tf.get(w) / b.length) * Math.log((1 + N) / (1 + df.get(w))) + 1e-9 : 0));
  });
}
function cosine(a, b) {
  let dot = 0, na = 0, nb = 0;
  for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i]; }
  return (na && nb) ? dot / (Math.sqrt(na) * Math.sqrt(nb)) : 0;
}
const NOVELTY_THRESHOLD = 0.65;
function auditPriorArt(text, corpus) {
  const db = corpus || PRIOR_ART_DB;
  const vecs = tfidfVectors([text, ...db.map(p => p.title + ' ' + p.abstract)]);
  const sims = vecs.slice(1).map(v => cosine(vecs[0], v));
  let max = 0, idx = -1;
  sims.forEach((s, i) => { if (s > max) { max = s; idx = i; } });
  const isNovel = max < NOVELTY_THRESHOLD;
  return {
    isNovel,
    novelty: +((1 - max) * 100).toFixed(2),
    similarity: +(max * 100).toFixed(2),
    match: idx >= 0 ? { ...db[idx], similarity: +(max * 100).toFixed(2) } : null,
    ranked: sims.map((s, i) => ({ ...db[i], similarity: +(s * 100).toFixed(2) })).sort((a, b) => b.similarity - a.similarity).slice(0, 3),
  };
}

/* ---------- internal hash chain (blockchain-ready) ---------- */
const GENESIS = '0'.repeat(64);
async function anchorBlock(chain, rec, audit) {
  const prev = chain.length ? chain[chain.length - 1] : null;
  const index = chain.length;
  const at = new Date().toISOString();
  const barcode = 'POAS-' + at.replace(/[-:TZ.]/g, '').slice(0, 14) + '-' + String(rec.hash || '').slice(0, 10).toUpperCase();
  const body = { index, prevHash: prev ? prev.hash : GENESIS, at, ref: rec.ref, poasHash: rec.hash, barcode, novelty: audit ? audit.novelty : null, score: rec.score };
  const hash = await sha256Hex(JSON.stringify(body, Object.keys(body).sort()));
  return { ...body, hash, title: rec.title };
}
/* ---------- live federated prior-art search (auto, no key, CORS-friendly) ---------- */
async function fetchJSON(url, ms = 7000) {
  const ctl = new AbortController();
  const to = setTimeout(() => ctl.abort(), ms);
  try { const r = await fetch(url, { signal: ctl.signal }); return r.ok ? await r.json() : null; }
  catch (e) { return null; }
  finally { clearTimeout(to); }
}
const LIVE_SOURCES = [
  { id: 'openalex', label: { ar: 'OpenAlex', en: 'OpenAlex' },
    run: async (q) => {
      const d = await fetchJSON('https://api.openalex.org/works?per-page=8&search=' + encodeURIComponent(q));
      return ((d && d.results) || []).map(w => ({
        id: (w.doi || w.id || '').replace('https://doi.org/', '').replace('https://openalex.org/', ''),
        title: w.title || '', abstract: w.title || '', year: w.publication_year,
        url: w.doi || w.id, src: 'OpenAlex',
      }));
    } },
  { id: 'epmc', label: { ar: 'Europe PMC', en: 'Europe PMC' },
    run: async (q) => {
      const d = await fetchJSON('https://www.ebi.ac.uk/europepmc/webservices/rest/search?format=json&pageSize=8&query=' + encodeURIComponent(q));
      return (((d || {}).resultList || {}).result || []).map(r => ({
        id: r.id, title: r.title || '', abstract: (r.abstractText || r.title || '').replace(/<[^>]+>/g, ''), year: r.pubYear,
        url: r.doi ? 'https://doi.org/' + r.doi : 'https://europepmc.org/article/' + r.source + '/' + r.id, src: 'Europe PMC',
      }));
    } },
  { id: 'crossref', label: { ar: 'Crossref', en: 'Crossref' },
    run: async (q) => {
      const d = await fetchJSON('https://api.crossref.org/works?rows=8&select=title,DOI,abstract,issued&query.bibliographic=' + encodeURIComponent(q));
      return ((((d || {}).message || {}).items) || []).map(w => ({
        id: w.DOI, title: (w.title || [''])[0], abstract: ((w.abstract || (w.title || [''])[0]) || '').replace(/<[^>]+>/g, ''),
        year: ((w.issued || {})['date-parts'] || [[]])[0][0], url: 'https://doi.org/' + w.DOI, src: 'Crossref',
      }));
    } },
  { id: 'patentsview_removed', label: { ar: 'PatentsView', en: 'PatentsView' }, disabled: true, run: async () => [] },
];
// note: no patent office exposes an anonymous, browser-reachable API (CORS/auth all blocked),
// so patent coverage = the local corpus for scoring + one-click deep links into the offices.

// query all sources in parallel, score every hit against the idea, return one ranked list
async function liveSearch(query) {
  const active = LIVE_SOURCES.filter(s => !s.disabled);
  const settled = await Promise.all(active.map(s => s.run(query).catch(() => [])));
  const hits = settled.flat().filter(h => h && h.title);
  const corpus = [...PRIOR_ART_DB.map(p => ({ id: p.id, title: p.title, abstract: p.abstract, src: 'Local', url: null })), ...hits];
  if (!corpus.length) return { hits: [], sources: active.length, live: 0 };
  const vecs = tfidfVectors([query, ...corpus.map(c => c.title + ' ' + c.abstract)]);
  const scored = corpus.map((c, i) => ({ ...c, similarity: +(cosine(vecs[0], vecs[i + 1]) * 100).toFixed(2) }))
    .sort((a, b) => b.similarity - a.similarity);
  return { hits: scored, live: hits.length, sources: active.length };
}

/* ---------- Solana anchoring (Anchor program poas_patent_registry) ---------- */
const SOLANA = {
  programId: 'POAsPatent111111111111111111111111111111111',
  cluster: 'devnet',
  instruction: 'register_patent',
  seeds: ['poas'],
  explorer: (sig) => `https://explorer.solana.com/address/${sig}?cluster=devnet`,
};
async function solanaWallet() {
  const p = window.solana;
  if (!p || !p.isPhantom) return { error: 'no-wallet' };
  try { const r = await p.connect(); return { address: r.publicKey.toString() }; }
  catch (e) { return { error: 'rejected' }; }
}
function solanaPayload(block, wallet) {
  return {
    network: 'solana',
    cluster: SOLANA.cluster,
    program_id: SOLANA.programId,
    instruction: SOLANA.instruction,
    args: {
      poas_hash: block.poasHash,
      barcode: block.barcode,
      novelty_score: Math.max(0, Math.min(255, Math.round(block.novelty == null ? 0 : block.novelty))),
    },
    accounts: {
      patent_record: { pda_seeds: [...SOLANA.seeds, block.ref], space: 8 + 64 + 32 + 1 + 8 + 32 },
      authority: wallet || '<connect wallet>',
      system_program: '11111111111111111111111111111111',
    },
    account_layout: 'PatentRecord { poas_hash: String, barcode: String, novelty_score: u8, timestamp: i64, owner: Pubkey }',
    ledger_mirror: { block_index: block.index, prev_hash: block.prevHash, block_hash: block.hash, anchored_at: block.at },
  };
}

function chainPayload(block, wallet) { return solanaPayload(block, wallet); }
async function verifyChain(chain) {
  for (let i = 0; i < chain.length; i++) {
    const b = chain[i];
    const body = { index: b.index, prevHash: b.prevHash, at: b.at, ref: b.ref, poasHash: b.poasHash, barcode: b.barcode, novelty: b.novelty, score: b.score };
    const h = await sha256Hex(JSON.stringify(body, Object.keys(body).sort()));
    if (h !== b.hash) return { ok: false, at: i };
    if (b.prevHash !== (i === 0 ? GENESIS : chain[i - 1].hash)) return { ok: false, at: i };
  }
  return { ok: true, at: -1 };
}

/* ---------- panel: audit → search → anchor ---------- */
function PriorArtPanel({ t, L, record, text, store, auth }) {
  const toast = useToast();
  const [audit, setAudit] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [block, setBlock] = React.useState(null);
  const [chk, setChk] = React.useState(null);
  const [wallet, setWallet] = React.useState(null);
  const [wErr, setWErr] = React.useState(null);
  const [live, setLive] = React.useState(null);
  const [scanning, setScanning] = React.useState(false);
  const chain = (store && store.data && store.data.chain) || [];
  const query = ((record && record.title) || '') + ' ' + (text || '');

  React.useEffect(() => { setAudit(null); setBlock(null); setChk(null); }, [record && record.hash]);

  // auto: as soon as an idea/title is typed, scan every source in parallel (debounced)
  const scan = React.useCallback(async (q) => {
    setScanning(true);
    const res = await liveSearch(q);
    setLive(res);
    const top = res.hits[0];
    const max = top ? top.similarity / 100 : 0;
    setAudit({ isNovel: max < NOVELTY_THRESHOLD, novelty: +((1 - max) * 100).toFixed(2), similarity: +(max * 100).toFixed(2), match: top || null, ranked: res.hits.slice(0, 5) });
    setScanning(false);
  }, []);

  React.useEffect(() => {
    const q = query.trim();
    if (q.length < 6) { setLive(null); setAudit(null); return; }
    const id = setTimeout(() => scan(q), 700);
    return () => clearTimeout(id);
  }, [query]);

  const anchor = async () => {
    if (!record) return;
    setBusy(true);
    const b = await anchorBlock(chain, record, audit);
    b.wallet = wallet || null;
    b.network = 'solana:' + SOLANA.cluster;
    const next = structuredClone(store.data);
    next.chain = [...chain, b];
    store.save(next);
    setBlock(b); setBusy(false);
    toast(t('chain_done'));
  };

  const verify = async () => { setChk(await verifyChain([...chain, ...(block && !chain.find(x => x.hash === block.hash) ? [block] : [])])); };

  return (
    <div className="card card-pad" style={{ padding: 30, display: 'grid', gridTemplateColumns: 'minmax(0,1fr)', gap: 20 }}>
      <div>
        <div className="eyebrow" style={{ color: 'var(--purple)' }}><Icon.shield s={15} /> {t('pa_eyebrow')}</div>
        <p className="muted" style={{ fontSize: 13.5, marginTop: 8, lineHeight: 1.7, maxWidth: 640 }}>{t('pa_sub')}</p>
      </div>

      <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
        <button className="btn btn-primary btn-sm" onClick={() => scan(query.trim())} disabled={scanning || query.trim().length < 6}>{scanning ? <><span className="spin" /> {t('pa_scanning')}</> : <><Icon.eye s={15} /> {t('pa_rescan')}</>}</button>
        {live && <span className="chip"><Icon.globe s={13} /> {t('pa_scanned')} {live.live} · {t('pa_lit')}</span>}
        {scanning && <span className="muted" style={{ fontSize: 12.5 }}>{t('pa_auto')}</span>}
      </div>

      {audit && (
        <div className="card" style={{ padding: 18, display: 'grid', gridTemplateColumns: 'minmax(0,1fr)', gap: 12, borderColor: `color-mix(in srgb, var(--${audit.isNovel ? 'mint' : 'rose'}) 34%, transparent)`, background: `color-mix(in srgb, var(--${audit.isNovel ? 'mint' : 'rose'}) 7%, transparent)` }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            <span className={'badge ' + (audit.isNovel ? 'mint' : 'rose')}>{audit.isNovel ? t('pa_pass') : t('pa_fail')}</span>
            <b style={{ fontSize: 15 }}>{t('pa_novelty')}: <span dir="ltr">{audit.novelty}%</span></b>
            <span className="muted" style={{ fontSize: 13 }} dir="ltr">max similarity {audit.similarity}%</span>
          </div>
          <div className="poas-meter"><span style={{ width: audit.novelty + '%', background: `var(--${audit.isNovel ? 'mint' : 'rose'})` }} /></div>
          {!audit.isNovel && <p style={{ fontSize: 13.5, lineHeight: 1.7, margin: 0 }}>{t('pa_warn')}</p>}
          <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1fr)', gap: 8 }}>
            {audit.ranked.map((r, ri) => (
              <div key={r.id + '-' + ri} style={{ display: 'flex', gap: 10, alignItems: 'center', padding: '9px 12px', border: '1px solid var(--line)', borderRadius: 'var(--r-sm)', background: 'rgba(255,255,255,.025)' }}>
                <span className="seal-code" dir="ltr" style={{ flex: 'none', maxWidth: 130, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{r.id}</span>
                {r.url ? (
                  <a href={r.url} target="_blank" rel="noopener" style={{ flex: 1, minWidth: 0, fontSize: 13.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} dir="ltr">{r.title}</a>
                ) : (
                  <span style={{ flex: 1, minWidth: 0, fontSize: 13.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} dir="ltr">{r.title}</span>
                )}
                {r.src && <span className="muted" style={{ flex: 'none', fontSize: 11.5 }} dir="ltr">{r.src}</span>}
                <span className={'badge ' + (r.similarity >= 65 ? 'rose' : 'purple')} style={{ flex: 'none', fontSize: 11 }} dir="ltr">{r.similarity}%</span>
              </div>
            ))}
          </div>
        </div>
      )}

      <div>
        <div className="eyebrow"><Icon.globe s={14} /> {t('pa_engines')}</div>
        <p className="muted" style={{ fontSize: 13, marginTop: 6, lineHeight: 1.65 }}>{t('pa_engines_d')}</p>
        <div style={{ display: 'flex', gap: 8, marginTop: 12, flexWrap: 'wrap' }}>
          {PATENT_SEARCH_ENGINES.map(e => (
            <a key={e.id} className="btn btn-ghost btn-sm" href={e.url(query.trim() || ' ')} target="_blank" rel="noopener"><Icon.globe s={14} /> {L(e.name)}</a>
          ))}
        </div>
      </div>

      <div>
        <div className="eyebrow" style={{ color: 'var(--gold)' }}><Icon.layers s={14} /> {t('chain_eyebrow')}</div>
        <p className="muted" style={{ fontSize: 13, marginTop: 6, lineHeight: 1.65 }}>{t('chain_d')}</p>
        <div style={{ display: 'flex', gap: 10, marginTop: 12, flexWrap: 'wrap' }}>
          <button className="btn btn-ghost btn-sm" onClick={() => {
            const sc = Number(record.score) || 0;
            window.printSealCertificate(
              { ref: record.ref, title: record.title, inventors: (auth && auth.user) ? L(auth.user.name) : '', kind: 'patent', field: record.classification || '' },
              { ref: record.ref, kind: 'patent', fingerprint: 'PoAS-v1::' + String(record.hash || '').slice(0, 10).toUpperCase() + '::Score:' + sc,
                hash: record.hash, sealedAt: (block && block.at) || new Date().toISOString(), version: 1.0,
                metrics: { INV: sc, INN: audit ? audit.novelty / 10 : sc }, score: sc, prevHash: block ? block.prevHash : null },
              L);
          }} disabled={!record || !record.hash}><Icon.book s={15} /> {t('vlt_pdf')}</button>
          <button className="btn btn-ghost btn-sm" onClick={async () => { const r = await solanaWallet(); if (r.address) { setWallet(r.address); setWErr(null); } else setWErr(r.error); }}>
            <Icon.globe s={15} /> {wallet ? <span dir="ltr">{wallet.slice(0, 4)}…{wallet.slice(-4)}</span> : t('sol_connect')}
          </button>
          <button className="btn btn-primary btn-sm" onClick={anchor} disabled={busy || !record || (audit && !audit.isNovel)}><Icon.lock s={15} /> {t('chain_anchor')}</button>
          <button className="btn btn-ghost btn-sm" onClick={verify} disabled={!chain.length && !block}><Icon.shield s={15} /> {t('chain_verify')}</button>
          <span className="chip"><Icon.hash s={13} /> {t('chain_blocks')} {chain.length + (block && !chain.find(x => x.hash === block.hash) ? 1 : 0)}</span>
        </div>
        {audit && !audit.isNovel && <p className="muted" style={{ fontSize: 12.5, marginTop: 10 }}>{t('chain_blocked')}</p>}
        {wErr && <p className="muted" style={{ fontSize: 12.5, marginTop: 10 }}>{wErr === 'no-wallet' ? t('sol_nowallet') : t('sol_rejected')}</p>}
        {chk && <div className={chk.ok ? 'cloud-msg ok' : 'cloud-msg err'} style={{ marginTop: 12 }}>{chk.ok ? <><Icon.check s={15} /> {t('chain_ok')}</> : <><Icon.shield s={15} /> {t('chain_bad')} #{chk.at}</>}</div>}
        {block && (
          <div className="card" style={{ padding: 16, marginTop: 12, display: 'grid', gap: 8 }}>
            {[[t('chain_block'), '#' + block.index], [t('chain_barcode'), block.barcode], [t('chain_hash'), block.hash], [t('chain_prev'), block.prevHash], [t('sol_program'), SOLANA.programId], [t('sol_pda'), '["poas", "' + block.ref + '"]'], [t('sol_wallet'), block.wallet || '—']].map(([k, v], i) => (
              <div key={i}><div className="muted" style={{ fontSize: 12 }}>{k}</div><div className="seal-code" dir="ltr" style={{ wordBreak: 'break-all' }}>{v}</div></div>
            ))}
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              <button className="btn btn-dark btn-sm" onClick={() => { copyText(JSON.stringify(chainPayload(block, block.wallet), null, 2)); toast(t('copied')); }}><Icon.layers s={14} /> {t('chain_payload')}</button>
              <a className="btn btn-ghost btn-sm" href={SOLANA.explorer(SOLANA.programId)} target="_blank" rel="noopener"><Icon.globe s={14} /> {t('sol_explorer')}</a>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { PRIOR_ART_DB, PATENT_SEARCH_ENGINES, auditPriorArt, liveSearch, LIVE_SOURCES, anchorBlock, chainPayload, solanaPayload, solanaWallet, SOLANA, verifyChain, PriorArtPanel });
