// ═════════════════════════════════════════════════════════════════════════════
// MindForge · play/resource-link.jsx
// ─────────────────────────────────────────────────────────────────────────────
// Role     : Deep-link launcher for shareable static decks. A link shaped like
//              mindforge.com/ressource=1dan_moduleA      (path form, via rewrite)
//              mindforge.com/?ressource=1dan_moduleA     (query form)
//              mindforge.com/#ressource=1dan_moduleA     (hash form)
//            is resolved against resources/manifest.json; the matching CSV is
//            imported through the editor's own CSV pipeline and offered as a
//            ready-to-play deck.
// Loaded   : last (after the store + editor, whose globals it reads)
// Reads    : window.{ loadLibrary, writeLibrary, normalizeCard, TONES,
//            BUILTIN_FIELDS, parseCSV, csvRowToCard }
// Exposes  : window.ResourceLink = { maybePrepare }
// ─────────────────────────────────────────────────────────────────────────────
// maybePrepare() runs BEFORE React mounts (see index.html). When a resource key
// is present it prompts the user, and on accept seeds localStorage with a manual
// deck holding the imported cards + marks it active — so the single useLibrary
// instance picks it up on first render and the app opens straight on that deck.
// Re-using an already-imported resource just re-activates it (idempotent link).
// ═════════════════════════════════════════════════════════════════════════════

(function () {
  const MANIFEST_URL = 'resources/manifest.json';

  // Accept ?ressource= / ?resource= (query), /ressource=KEY (path), or
  // #ressource=KEY (hash). One spelling, with or without the second "s".
  const KEY_RX = /ress?ource=([\w.-]+)/i;

  const detectKey = () => {
    const qp = new URLSearchParams(location.search);
    const fromQuery = qp.get('ressource') || qp.get('resource');
    if (fromQuery) return fromQuery;
    const m = (location.pathname + location.hash).match(KEY_RX);
    return m ? m[1] : null;
  };

  // Drop the resource marker from the address bar and land on the play home
  // (empty hash), without adding a history entry.
  const cleanUrl = () => {
    try { history.replaceState(null, '', location.origin + '/'); } catch {}
  };

  const fetchJSON = async (url) => {
    const res = await fetch(url, { cache: 'no-cache' });
    if (!res.ok) throw new Error('HTTP ' + res.status);
    return res.json();
  };

  const fetchText = async (url) => {
    const res = await fetch(url, { cache: 'no-cache' });
    if (!res.ok) throw new Error('HTTP ' + res.status);
    return res.text();
  };

  // CSV text → importable card partials, via the editor's exact pipeline.
  const parseDeckCsv = (text) => {
    const fields = (typeof BUILTIN_FIELDS !== 'undefined') ? BUILTIN_FIELDS : [];
    const { rows } = parseCSV(text);
    return rows
      .map(r => csvRowToCard(r, fields))
      .filter(c => c.fr || c.kr);
  };

  // Merge imported cards into the stored library as one manual deck and mark it
  // active. Re-importing the same resource replaces its previous cards so a CSV
  // update propagates instead of duplicating. Returns the deck id.
  const seedLibraryWithDeck = (entry, partials) => {
    const deckId = 'res-' + entry.key;
    const lib = loadLibrary();

    let cards = lib.cards;
    let decks = lib.decks;
    const prev = decks.find(d => d.id === deckId);
    if (prev) {
      const ownIds = new Set(prev.cardIds || []);
      cards = cards.filter(c => !ownIds.has(c.id));
      decks = decks.filter(d => d.id !== deckId);
    }

    const maxId = cards.reduce((m, c) => Math.max(m, c.id || 0), 0);
    const added = partials.map((p, i) => normalizeCard({
      id: maxId + 1 + i,
      tone: TONES[(maxId + 1 + i) % TONES.length],
      ...p,
    }));
    const deck = {
      id: deckId,
      name: entry.name || entry.key,
      sub: entry.sub || '',
      type: 'manual',
      cardIds: added.map(c => c.id),
    };

    writeLibrary({
      ...lib,
      cards: [...cards, ...added],
      decks: [...decks, deck],
      activeDeckId: deckId,
    });
    return deckId;
  };

  // Just re-activate an already-imported resource deck (no re-import needed).
  const activateExisting = (deckId) => {
    const lib = loadLibrary();
    if (!lib.decks.find(d => d.id === deckId)) return false;
    writeLibrary({ ...lib, activeDeckId: deckId });
    return true;
  };

  // ── Minimal styled prompt (runs before React, so it's plain DOM) ──────────
  const showPrompt = ({ name, sub, count }) => new Promise((resolve) => {
    const root = document.createElement('div');
    root.setAttribute('role', 'dialog');
    root.style.cssText =
      'position:fixed;inset:0;z-index:9999;display:flex;align-items:center;' +
      'justify-content:center;padding:24px;' +
      'background:rgba(6,5,10,.72);backdrop-filter:blur(6px);' +
      "font-family:'Inter',-apple-system,system-ui,sans-serif";

    const cardCount = count === 1 ? '1 carte' : count + ' cartes';
    root.innerHTML =
      '<div style="width:100%;max-width:360px;background:#0e0d12;border:1px solid rgba(255,255,255,.08);' +
      'border-radius:22px;padding:24px 22px;box-shadow:0 30px 80px rgba(0,0,0,.55);text-align:center">' +
        '<div style="width:54px;height:54px;margin:0 auto 14px;border-radius:16px;' +
        'background:linear-gradient(135deg,#a78bfa,#7fb4ff);display:flex;align-items:center;' +
        'justify-content:center;font-size:26px">📚</div>' +
        '<div style="font-size:12px;font-weight:600;letter-spacing:.12em;color:#6c6880;text-transform:uppercase">Deck partagé</div>' +
        '<div style="font-size:21px;font-weight:800;color:#ece9f5;margin-top:6px;letter-spacing:-.3px">' + escapeHtml(name) + '</div>' +
        (sub ? '<div style="font-size:13.5px;color:#a39fb5;margin-top:4px">' + escapeHtml(sub) + '</div>' : '') +
        '<div style="font-size:12.5px;color:#6c6880;margin-top:10px">' + cardCount + ' à importer</div>' +
        '<div style="display:flex;gap:10px;margin-top:22px">' +
          '<button data-act="cancel" style="flex:1;padding:12px;border-radius:14px;cursor:pointer;' +
          'border:1px solid rgba(255,255,255,.1);background:transparent;color:#a39fb5;font-weight:600;font-size:14px">Annuler</button>' +
          '<button data-act="launch" style="flex:1.4;padding:12px;border-radius:14px;cursor:pointer;border:none;' +
          'background:linear-gradient(135deg,#a78bfa,#7fb4ff);color:#0b0a10;font-weight:800;font-size:14px">Lancer le deck</button>' +
        '</div>' +
      '</div>';

    const done = (v) => { try { document.body.removeChild(root); } catch {} resolve(v); };
    root.addEventListener('click', (e) => {
      const act = e.target.getAttribute && e.target.getAttribute('data-act');
      if (act === 'launch') done(true);
      else if (act === 'cancel' || e.target === root) done(false);
    });
    document.body.appendChild(root);
  });

  const escapeHtml = (s) => String(s).replace(/[&<>"']/g, (c) => (
    { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]
  ));

  // ── Entry point ───────────────────────────────────────────────────────────
  // Resolves once the deep-link has been handled (or determined absent). Never
  // rejects — any failure falls through to the normal app silently.
  async function maybePrepare() {
    const key = detectKey();
    if (!key) return;

    let manifest;
    try { manifest = await fetchJSON(MANIFEST_URL); }
    catch { return; }   // no manifest → behave like a normal visit

    const entry = (manifest.resources || []).find(
      r => String(r.key).toLowerCase() === String(key).toLowerCase()
    );
    if (!entry) { cleanUrl(); return; }   // unknown key → ignore the marker

    let partials;
    try { partials = parseDeckCsv(await fetchText(entry.file)); }
    catch { cleanUrl(); return; }
    if (!partials.length) { cleanUrl(); return; }

    const launch = await showPrompt({
      name: entry.name || entry.key,
      sub: entry.sub,
      count: partials.length,
    });
    if (!launch) { cleanUrl(); return; }

    const deckId = 'res-' + entry.key;
    const reused = activateExisting(deckId);
    if (!reused) seedLibraryWithDeck(entry, partials);
    if (window.mfTrack) window.mfTrack('deck_launched', { key: entry.key, cards: partials.length, reused: reused });
    cleanUrl();
  }

  window.ResourceLink = { maybePrepare };
})();
