// bulk-reg-validate.jsx — Bulk Registration Validation
// ─────────────────────────────────────────────────────────────────
// Per-format pre-flight validation. Runs BEFORE build() so the user
// sees errors at the entity level, not in the rendered file.
//
// Returns: { ok, errors:[{severity, code, msg, entityId, entityType, hint}] }
//
// Severity ladder:
//   blocker — must fix before build (won't pass society validation)
//   error   — society will reject this row but file builds
//   warn    — society may flag; recommended to fix
//   info    — informational
//
// Exports: window.BULK_REG_VALIDATE
// ─────────────────────────────────────────────────────────────────
(function () {
  if (typeof window === 'undefined') return;
  const E = window.BULK_REG_ENGINE;
  if (!E) { console.warn('[bulk-reg-validate] BULK_REG_ENGINE missing'); return; }

  // ── helpers ────────────────────────────────────────────────────
  const ISWC_RE = /^T\d{10}$/;
  const ISRC_RE = /^[A-Z]{2}[A-Z0-9]{3}\d{7}$/;
  const IPI_RE  = /^\d{9,11}$/;
  const UPC_RE  = /^\d{12,14}$/;

  function shareSum(arr, key) {
    return (arr || []).reduce((s, x) => s + (Number(x[key]) || 0), 0);
  }

  // ── universal work-level checks ────────────────────────────────
  function validateWork(w) {
    const errs = [];
    if (!w.title || !w.title.trim()) errs.push({ severity: 'blocker', code: 'WK-001', msg: 'Missing title', entityId: w.workId, entityType: 'work' });
    if (w.iswc && !ISWC_RE.test(w.iswc)) errs.push({ severity: 'error', code: 'WK-002', msg: `Invalid ISWC format: ${w.iswc}`, entityId: w.workId, entityType: 'work', hint: 'ISWC = T followed by 10 digits' });
    if (!w.writers || !w.writers.length) errs.push({ severity: 'blocker', code: 'WK-003', msg: 'No writers', entityId: w.workId, entityType: 'work' });
    const prSum = shareSum(w.writers, 'prShare');
    const mrSum = shareSum(w.writers, 'mrShare');
    if (w.writers && w.writers.length && Math.abs(prSum - 100) > 0.01 && prSum > 0) {
      errs.push({ severity: 'error', code: 'WK-004', msg: `Writer PR shares sum to ${prSum.toFixed(2)}, not 100`, entityId: w.workId, entityType: 'work' });
    }
    if (w.writers && w.writers.length && Math.abs(mrSum - 100) > 0.01 && mrSum > 0) {
      errs.push({ severity: 'error', code: 'WK-005', msg: `Writer MR shares sum to ${mrSum.toFixed(2)}, not 100`, entityId: w.workId, entityType: 'work' });
    }
    (w.writers || []).forEach((wr, i) => {
      if (!wr.name) errs.push({ severity: 'error', code: 'WK-010', msg: `Writer ${i + 1} missing name`, entityId: w.workId, entityType: 'work' });
      if (wr.ipi && !IPI_RE.test(wr.ipi)) errs.push({ severity: 'warn', code: 'WK-011', msg: `Writer ${i + 1} IPI malformed: ${wr.ipi}`, entityId: w.workId, entityType: 'work' });
      (wr.publishers || []).forEach((p, j) => {
        if (!p.name) errs.push({ severity: 'error', code: 'WK-012', msg: `Writer ${i + 1} publisher ${j + 1} missing name`, entityId: w.workId, entityType: 'work' });
        if (p.ipi && !IPI_RE.test(p.ipi)) errs.push({ severity: 'warn', code: 'WK-013', msg: `Publisher ${p.name} IPI malformed`, entityId: w.workId, entityType: 'work' });
      });
    });
    return errs;
  }

  function validateRecording(r) {
    const errs = [];
    if (!r.isrc) errs.push({ severity: 'blocker', code: 'RC-001', msg: 'Missing ISRC', entityId: r.recId, entityType: 'recording' });
    else if (!ISRC_RE.test(r.isrc)) errs.push({ severity: 'error', code: 'RC-002', msg: `Invalid ISRC: ${r.isrc}`, entityId: r.recId, entityType: 'recording', hint: 'ISRC = CC-XXX-YY-NNNNN (12 chars)' });
    if (!r.title) errs.push({ severity: 'blocker', code: 'RC-003', msg: 'Missing title', entityId: r.recId, entityType: 'recording' });
    if (!r.artist) errs.push({ severity: 'error', code: 'RC-004', msg: 'Missing artist', entityId: r.recId, entityType: 'recording' });
    if (!r.duration || r.duration < 1) errs.push({ severity: 'warn', code: 'RC-005', msg: 'Missing or zero duration', entityId: r.recId, entityType: 'recording' });
    if (!r.label) errs.push({ severity: 'warn', code: 'RC-006', msg: 'Missing label / rights owner', entityId: r.recId, entityType: 'recording' });
    if (!r.pLineYear) errs.push({ severity: 'warn', code: 'RC-007', msg: 'Missing P-line year', entityId: r.recId, entityType: 'recording' });
    return errs;
  }

  function validateRelease(rel) {
    const errs = [];
    if (!rel.upc) errs.push({ severity: 'blocker', code: 'RL-001', msg: 'Missing UPC', entityId: rel.relId, entityType: 'release' });
    else if (!UPC_RE.test(rel.upc)) errs.push({ severity: 'error', code: 'RL-002', msg: `Invalid UPC: ${rel.upc}`, entityId: rel.relId, entityType: 'release', hint: 'UPC = 12, 13, or 14 digits' });
    if (!rel.title) errs.push({ severity: 'blocker', code: 'RL-003', msg: 'Missing release title', entityId: rel.relId, entityType: 'release' });
    if (!rel.artist) errs.push({ severity: 'error', code: 'RL-004', msg: 'Missing display artist', entityId: rel.relId, entityType: 'release' });
    if (!rel.releaseDate) errs.push({ severity: 'warn', code: 'RL-005', msg: 'Missing release date', entityId: rel.relId, entityType: 'release' });
    if (!rel.label) errs.push({ severity: 'warn', code: 'RL-006', msg: 'Missing label', entityId: rel.relId, entityType: 'release' });
    return errs;
  }

  // ── format-specific overrides ──────────────────────────────────
  const FORMAT_RULES = {
    'mlc-bdf': (entities) => {
      const errs = [];
      entities.forEach((w) => {
        validateWork(w).forEach((e) => errs.push(e));
        if (!w.iswc) errs.push({ severity: 'warn', code: 'MLC-001', msg: 'Missing ISWC — MLC strongly prefers ISWC for matching', entityId: w.workId, entityType: 'work' });
      });
      return errs;
    },
    'hfa-songfile': (entities) => {
      const errs = [];
      entities.forEach((w) => {
        validateWork(w).forEach((e) => errs.push(e));
        if ((w.writers || []).length > 2) errs.push({ severity: 'warn', code: 'HFA-001', msg: `${w.writers.length} writers — Songfile template only fits 2`, entityId: w.workId, entityType: 'work', hint: 'Use full Songfile XML format for >2 writers' });
      });
      return errs;
    },
    'sx-isrc': (entities) => {
      const errs = [];
      entities.forEach((r) => validateRecording(r).forEach((e) => errs.push(e)));
      return errs;
    },
    'xperi-tivo': (entities) => {
      const errs = [];
      entities.forEach((r) => {
        validateRecording(r).forEach((e) => errs.push(e));
        if (!r.iswc) errs.push({ severity: 'info', code: 'XPR-001', msg: 'Linking ISWC missing — Xperi can match with title+artist but ISWC is preferred', entityId: r.recId, entityType: 'recording' });
      });
      return errs;
    },
    'ddex-mwn': (entities) => {
      const errs = [];
      entities.forEach((w) => {
        validateWork(w).forEach((e) => errs.push(e));
        if (!w.iswc) errs.push({ severity: 'error', code: 'MWN-001', msg: 'DDEX MWN requires ISWC for unambiguous work ID', entityId: w.workId, entityType: 'work' });
      });
      return errs;
    },
    'ddex-ern': (entities) => {
      const errs = [];
      entities.forEach((rel) => validateRelease(rel).forEach((e) => errs.push(e)));
      return errs;
    },
    'ddex-rdrn': (entities) => {
      const errs = [];
      entities.forEach((r) => validateRecording(r).forEach((e) => errs.push(e)));
      return errs;
    },
    'pro-direct': (entities, opts) => {
      const errs = [];
      entities.forEach((w) => {
        validateWork(w).forEach((e) => errs.push(e));
      });
      if (opts && opts.society === 'BMI' && !opts.submitterPubNumber) {
        errs.push({ severity: 'blocker', code: 'PRO-001', msg: 'BMI submission requires Submitter Publisher Number' });
      }
      return errs;
    },
  };

  function validate(formatId, selectionModel, opts = {}) {
    const adapter = E.ADAPTERS[formatId];
    if (!adapter) return { ok: false, errors: [{ severity: 'blocker', code: 'X-000', msg: 'Unknown format' }] };
    const entities = E.selectEntities(adapter.entity, selectionModel, { formatId });
    if (!entities.length) return { ok: false, entities: [], errors: [{ severity: 'blocker', code: 'X-001', msg: 'No entities selected' }] };
    const fn = FORMAT_RULES[formatId];
    const errors = fn ? fn(entities, opts) : [];
    const blockers = errors.filter((e) => e.severity === 'blocker').length;
    return {
      ok: blockers === 0,
      entities,
      errors,
      counts: {
        blocker: blockers,
        error: errors.filter((e) => e.severity === 'error').length,
        warn: errors.filter((e) => e.severity === 'warn').length,
        info: errors.filter((e) => e.severity === 'info').length,
      },
    };
  }

  window.BULK_REG_VALIDATE = { validate, validateWork, validateRecording, validateRelease };
})();
