/* global React, WORKS, RECORDINGS, RELEASES */
// statements-bridge.jsx
// ─────────────────────────────────────────────────────────────────────
// Decodes the packed statement data in window.__STMT_RAW into normalized
// in-memory structures that royalties.jsx + statement-drawer.jsx consume.
//
// Outputs (all on window):
//   __STMT_INDEX  — { statements: [{...}], byId: {id: stmt}, bySource: {srcId: [stmt...]}, byPeriod: {pid: [stmt...]} }
//
// Each statement has:
//   id, sourceId, sourceName, sourceKind, sourceColor, period, periodLabel,
//   currency, grossUsd, lineCount, legalEntity, processedDate, closedDate,
//   lines: [...] — fully decoded line objects
//   summary: { byDsp, byTerritory, byTitle, byContentType, byActivityPeriod, byDelivery,
//              byPerfSource, byCountry, byRoleType, etc. }
//   linksWorks: [workId, ...]    (matched from existing window.WORKS)
//   linksRecordings: [recId, ...] (matched from existing window.RECORDINGS by ISRC)
// ─────────────────────────────────────────────────────────────────────

(function statementsBridge() {
  const RAW = window.__STMT_RAW;
  if (!RAW || !RAW.statements) {
    console.warn('[statements-bridge] No __STMT_RAW found — skipping');
    return;
  }

  // ─── Decoders ────────────────────────────────────────────────────
  function decodeRSDLines(stmt) {
    const D = stmt.dict;
    const get = (dict, idx) => idx == null || idx < 0 ? '' : dict[idx];
    return stmt.linesPacked.map((p, i) => ({
      id: i,
      label: get(D.labels, p[0]),
      releaseName: p[1],
      releaseVersion: get(D.releaseVersions, p[2]),
      releaseArtists: p[3],
      upc: p[4],
      catalogue: get(D.catalogues, p[5]),
      trackTitle: p[6],
      mixVersion: get(D.mixVersions, p[7]),
      isrc: p[8],
      trackArtists: p[9],
      dsp: get(D.dsps, p[10]),
      activityPeriod: get(D.activityPeriods, p[11]),
      territory: get(D.territories, p[12]),
      delivery: get(D.deliveries, p[13]),
      contentType: get(D.contentTypes, p[14]),
      saleOrVoid: get(D.saleOrVoid, p[15]),
      count: p[16],
      royaltyUsd: p[17],
    }));
  }

  // ─── Cross-link helpers ────────────────────────────────────────
  // Build lookup tables once from existing global catalog data
  function buildLookups() {
    const byIsrc = new Map();
    const byTitle = new Map();
    const RECS = window.RECORDINGS || [];
    const WKS = window.WORKS || [];
    for (const r of RECS) {
      if (r.isrc) byIsrc.set(r.isrc.replace(/[\s-]/g,'').toUpperCase(), r);
      // Also index any aliased ISRCs
      const aliases = r.__rsAliasIsrcs || [];
      for (const a of aliases) byIsrc.set(String(a).replace(/[\s-]/g,'').toUpperCase(), r);
    }
    for (const w of WKS) {
      const t = (w.title || '').trim().toUpperCase();
      if (t && !byTitle.has(t)) byTitle.set(t, w);
    }
    return { byIsrc, byTitle };
  }

  // ─── Summary builders per parser kind ────────────────────────
  function summarizeRSD(lines) {
    const groupBy = (key, valFn = (l) => l.royaltyUsd) => {
      const m = new Map();
      for (const l of lines) {
        const k = l[key]; if (!k) continue;
        const v = valFn(l);
        if (!m.has(k)) m.set(k, { key:k, gross:0, count:0, units:0 });
        const x = m.get(k); x.gross += v; x.count += 1; x.units += (l.count||0);
      }
      return [...m.values()].sort((a,b) => b.gross - a.gross);
    };
    return {
      byDsp: groupBy('dsp'),
      byTerritory: groupBy('territory'),
      byContentType: groupBy('contentType'),
      byDelivery: groupBy('delivery'),
      byActivityPeriod: groupBy('activityPeriod'),
      byLabel: groupBy('label'),
      byCatalogue: groupBy('catalogue'),
      byTrack: (() => {
        const m = new Map();
        for (const l of lines) {
          const k = l.isrc || l.trackTitle; if (!k) continue;
          if (!m.has(k)) m.set(k, { key: k, isrc: l.isrc, trackTitle: l.trackTitle, releaseName: l.releaseName,
            artist: l.trackArtists, gross: 0, count: 0, units: 0, dsps: new Set(), territories: new Set() });
          const x = m.get(k); x.gross += l.royaltyUsd; x.count++; x.units += (l.count||0);
          if (l.dsp) x.dsps.add(l.dsp); if (l.territory) x.territories.add(l.territory);
        }
        return [...m.values()].map(x => ({ ...x, dsps: [...x.dsps], territories: [...x.territories] }))
          .sort((a,b) => b.gross - a.gross);
      })(),
    };
  }

  function summarizeBMI(lines) {
    const groupBy = (key) => {
      const m = new Map();
      for (const l of lines) {
        const k = l[key]; if (!k) continue;
        if (!m.has(k)) m.set(k, { key:k, gross:0, count:0, perfCount:0 });
        const x = m.get(k); x.gross += l.royaltyUsd; x.count += 1; x.perfCount += (l.perfCount||0);
      }
      return [...m.values()].sort((a,b) => b.gross - a.gross);
    };
    return {
      byPerfSource: groupBy('perfSource'),
      byCountry: groupBy('country'),
      byRole: groupBy('role'),
      byUseCode: groupBy('useCode'),
      byTitle: (() => {
        const m = new Map();
        for (const l of lines) {
          const k = l.titleName; if (!k) continue;
          if (!m.has(k)) m.set(k, { key: k, titleName: l.titleName, titleNumber: l.titleNumber,
            gross: 0, count: 0, perfCount: 0, sources: new Set(), pct: l.participantPct });
          const x = m.get(k); x.gross += l.royaltyUsd; x.count++; x.perfCount += (l.perfCount||0);
          if (l.perfSource) x.sources.add(l.perfSource);
        }
        return [...m.values()].map(x => ({ ...x, sources: [...x.sources] }))
          .sort((a,b) => b.gross - a.gross);
      })(),
    };
  }

  function summarizeASCAPForeign(lines) {
    const groupBy = (key) => {
      const m = new Map();
      for (const l of lines) {
        const k = l[key]; if (!k) continue;
        if (!m.has(k)) m.set(k, { key:k, gross:0, count:0 });
        const x = m.get(k); x.gross += l.royaltyUsd; x.count += 1;
      }
      return [...m.values()].sort((a,b) => b.gross - a.gross);
    };
    return {
      byCountry: groupBy('country'),
      byLicensor: groupBy('licensor'),
      byRevenueClass: groupBy('revenueClassDesc'),
      byTitle: (() => {
        const m = new Map();
        for (const l of lines) {
          const k = l.workTitle; if (!k) continue;
          if (!m.has(k)) m.set(k, { key:k, workTitle:l.workTitle, gross:0, count:0,
            countries: new Set(), licensors: new Set() });
          const x = m.get(k); x.gross += l.royaltyUsd; x.count++;
          if (l.country) x.countries.add(l.country); if (l.licensor) x.licensors.add(l.licensor);
        }
        return [...m.values()].map(x => ({ ...x, countries: [...x.countries], licensors: [...x.licensors] }))
          .sort((a,b) => b.gross - a.gross);
      })(),
    };
  }

  function summarizeHFA(lines) {
    const groupBy = (key) => {
      const m = new Map();
      for (const l of lines) {
        const k = l[key]; if (!k) continue;
        if (!m.has(k)) m.set(k, { key:k, gross:0, count:0, units:0 });
        const x = m.get(k); x.gross += l.grossAmount; x.count += 1; x.units += (l.units||0);
      }
      return [...m.values()].sort((a,b) => b.gross - a.gross);
    };
    return {
      byLicensee: groupBy('licenseeName'),
      byConfigCode: groupBy('configCode'),
      byConfigGroup: groupBy('configGroup'),
      byTerritory: groupBy('territory'),
      bySaleType: groupBy('licenseeSaleType'),
      byTxnDescription: groupBy('txnDescription'),
      byTitle: (() => {
        const m = new Map();
        for (const l of lines) {
          const k = l.songTitle; if (!k) continue;
          if (!m.has(k)) m.set(k, { key:k, songTitle:l.songTitle, isrc:l.isrc, iswc:l.iswc, hfaSongCode:l.hfaSongCode,
            artist:l.artists, album:l.album,
            gross:0, royalty:0, count:0, units:0,
            licensees: new Set(), territories: new Set() });
          const x = m.get(k); x.gross += (l.grossAmount||0); x.royalty += (l.royaltyAmount||0); x.count++; x.units += (l.units||0);
          if (l.licenseeName) x.licensees.add(l.licenseeName);
          if (l.territory) x.territories.add(l.territory);
        }
        return [...m.values()].map(x => ({ ...x, licensees: [...x.licensees], territories: [...x.territories] }))
          .sort((a,b) => b.gross - a.gross);
      })(),
    };
  }

  function summarizeTikTok(lines) {
    const groupBy = (key, valFn = l => l.royaltyAmount) => {
      const m = new Map();
      for (const l of lines) {
        const k = l[key]; if (!k) continue;
        if (!m.has(k)) m.set(k, { key:k, gross:0, count:0, creations:0, weightedCreations:0 });
        const x = m.get(k); x.gross += valFn(l); x.count += 1;
        x.creations += (l.creations||0); x.weightedCreations += (l.weightedCreations||0);
      }
      return [...m.values()].sort((a,b) => b.gross - a.gross);
    };
    return {
      byPublisher: groupBy('publisher'),
      byMonth: groupBy('month'),
      byArtist: groupBy('artist'),
      byLabel: groupBy('label'),
      byTitle: (() => {
        const m = new Map();
        for (const l of lines) {
          const k = l.compositionTitle; if (!k) continue;
          if (!m.has(k)) m.set(k, { key:k, compositionTitle:l.compositionTitle, mriSongId:l.mriSongId,
            isrc:l.isrc, artist:l.artist, label:l.label, composers:l.composers,
            gross:0, count:0, creations:0, weightedCreations:0,
            months: new Set(), publishers: new Set() });
          const x = m.get(k); x.gross += l.royaltyAmount; x.count++;
          x.creations += (l.creations||0); x.weightedCreations += (l.weightedCreations||0);
          if (l.month) x.months.add(l.month); if (l.publisher) x.publishers.add(l.publisher);
        }
        return [...m.values()].map(x => ({ ...x, months: [...x.months], publishers: [...x.publishers] }))
          .sort((a,b) => b.gross - a.gross);
      })(),
    };
  }

  function summarizeASCAPIntl(lines) {
    const groupBy = (key) => {
      const m = new Map();
      for (const l of lines) {
        const k = l[key]; if (!k) continue;
        if (!m.has(k)) m.set(k, { key:k, gross:0, count:0, plays:0 });
        const x = m.get(k); x.gross += l.royaltyUsd; x.count += 1; x.plays += (l.plays||0);
      }
      return [...m.values()].sort((a,b) => b.gross - a.gross);
    };
    return {
      byMusicUser: groupBy('musicUser'),
      byPerfSource: groupBy('perfSource'),
      byTerritory: groupBy('territory'),
      byLicensor: groupBy('licensor'),
      byRightType: groupBy('rightType'),
      byTitle: (() => {
        const m = new Map();
        for (const l of lines) {
          const k = l.workTitle; if (!k) continue;
          if (!m.has(k)) m.set(k, { key:k, workTitle:l.workTitle, gross:0, count:0, plays:0,
            users: new Set(), territories: new Set() });
          const x = m.get(k); x.gross += l.royaltyUsd; x.count++; x.plays += (l.plays||0);
          if (l.musicUser) x.users.add(l.musicUser); if (l.territory) x.territories.add(l.territory);
        }
        return [...m.values()].map(x => ({ ...x, users: [...x.users], territories: [...x.territories] }))
          .sort((a,b) => b.gross - a.gross);
      })(),
    };
  }

  // ─── Main decode ────────────────────────────────────────────────
  const lookups = buildLookups();
  const decoded = [];

  for (const r of RAW.statements) {
    let lines, summary;
    if (r.parser === 'rsd-distributor') {
      lines = decodeRSDLines(r);
      summary = summarizeRSD(lines);
    } else if (r.parser === 'bmi') {
      lines = r.lines;
      summary = summarizeBMI(lines);
    } else if (r.parser === 'ascap-foreign') {
      lines = r.lines;
      summary = summarizeASCAPForeign(lines);
    } else if (r.parser === 'ascap-intl') {
      lines = r.lines || r.detail || [];
      summary = summarizeASCAPIntl(lines);
    } else if (r.parser === 'hfa-mech') {
      lines = r.detail || r.lines || [];
      summary = summarizeHFA(lines);
    } else if (r.parser === 'tiktok-mri') {
      lines = r.detail || r.lines || [];
      summary = summarizeTikTok(lines);
    } else {
      continue;
    }

    // Cross-link lines to existing catalog
    const linkedWorks = new Set();
    const linkedRecs = new Set();
    let matchedCount = 0;

    for (const l of lines) {
      // ISRC-based recording match (RSD distributor lines have ISRCs)
      if (l.isrc) {
        const k = String(l.isrc).replace(/[\s-]/g,'').toUpperCase();
        const rec = lookups.byIsrc.get(k);
        if (rec) {
          l.__matchedRecordingId = rec.id;
          linkedRecs.add(rec.id);
          matchedCount++;
          if (rec.workId) { l.__matchedWorkId = rec.workId; linkedWorks.add(rec.workId); }
          continue;
        }
      }
      // Title-based work match (PRO lines have work titles)
      const t = (l.titleName || l.workTitle || l.trackTitle || l.songTitle || l.compositionTitle || '').trim().toUpperCase();
      if (t) {
        const w = lookups.byTitle.get(t);
        if (w) {
          l.__matchedWorkId = w.id;
          linkedWorks.add(w.id);
          matchedCount++;
        }
      }
    }

    decoded.push({
      ...r,
      lines,
      summary,
      linkedWorks: [...linkedWorks],
      linkedRecordings: [...linkedRecs],
      matchedLineCount: matchedCount,
      matchedRate: lines.length ? matchedCount / lines.length : 0,
      // Fields for compat with synthetic STATEMENTS shape (royalties.jsx):
      gross: r.grossUsd,
      lineCountTotal: lines.length,
      anomalies: lines.length - matchedCount,
      isReal: true,
    });
  }

  // Indexes
  const byId = {};
  const bySource = {};
  const byPeriod = {};
  for (const s of decoded) {
    byId[s.id] = s;
    (bySource[s.sourceId] = bySource[s.sourceId] || []).push(s);
    (byPeriod[s.period] = byPeriod[s.period] || []).push(s);
  }

  window.__STMT_INDEX = {
    statements: decoded,
    byId, bySource, byPeriod,
    totalUsd: decoded.reduce((a,s) => a + s.grossUsd, 0),
    totalLines: decoded.reduce((a,s) => a + s.lines.length, 0),
  };
  window.__STMT_LOADED = true;

  console.log('[statements-bridge] Loaded',
    decoded.length, 'statements ·',
    decoded.reduce((a,s) => a + s.lines.length, 0).toLocaleString(), 'lines · $' +
    decoded.reduce((a,s) => a + s.grossUsd, 0).toFixed(2));

  // Notify listeners
  window.dispatchEvent(new CustomEvent('astro-statements-loaded'));
})();
