// ============================================================================
// CWR FIELD DECODER  ·  per-version field maps for inspecting a record
// ----------------------------------------------------------------------------
// Given a raw CWR line (the first 3 chars are the record type), we slice it
// into named fields. Used by the inspector panel — when the user clicks a
// line, the right-hand panel decodes every field, including looking up
// codes against the ref-data tables (ROLES, designations, types, etc).
//
// EXPORT: window.CwrDecode = { fieldsFor, decodeLine, lookupCode }
// ============================================================================

(function () {
  'use strict';

  // ─── Field schema generator ───────────────────────────────────────────────
  // Each entry: [name, length, kind, lookup?]
  //   kind: 'text' | 'num' | 'date' | 'time' | 'flag' | 'ipi' | 'iswc'
  //   lookup: ref-data key e.g. 'cwr_writer_designations' (matches code field)
  // We define for the BASE 2.1 layout; extra fields for 3.x are appended.
  const SCHEMA = {
    HDR: (v) => [
      ['Record type', 3, 'text'],
      ['Sender type', 2, 'text', 'cwr_sender_types'],
      ['Sender ID', 9, 'num'],
      ['Sender name', 45, 'text'],
      ['EDI standard ver', 5, 'text'],
      ['Creation date', 8, 'date'],
      ['Creation time', 6, 'time'],
      ['Transmission date', 8, 'date'],
      ...(v === '3.0' || v === '3.1' ? [['Charset', 2, 'text']] : []),
    ],
    GRH: (v) => [
      ['Record type', 3, 'text'],
      ['Transaction type', 3, 'text', 'cwr_transaction_types'],
      ['Group ID', 5, 'num'],
      ['Version number', 5, 'text'],
      ...(v === '2.1' || v === '2.1r7' || v === '2.2'
        ? [['Batch request', 10, 'num'], ['Submission/Distribution', 2, 'text'], ['Reserved', 13, 'text']]
        : []),
    ],
    GRT: () => [
      ['Record type', 3, 'text'],
      ['Group ID', 5, 'num'],
      ['Transaction count', 8, 'num'],
      ['Record count', 8, 'num'],
      ['Currency indicator', 3, 'text'],
      ['Total monetary value', 10, 'num'],
      ['Reserved', 10, 'text'],
    ],
    TRL: () => [
      ['Record type', 3, 'text'],
      ['Group count', 5, 'num'],
      ['Transaction count', 8, 'num'],
      ['Record count', 7, 'num'],
    ],
    NWR: nwrLike,
    REV: nwrLike,
    ISW: nwrLike,
    EXC: nwrLike,
    SPU: pubLike,
    OPU: pubLike,
    SPT: terLike,
    OPT: terLike,
    SWR: wrLike,
    OWR: wrLike,
    SWT: wtLike,
    OWT: wtLike,
    PWR: (v) => [
      header(),
      ['Publisher IP num', 9, 'text'],
      ['Publisher name', 45, 'text'],
      ['Submitter agreement #', 14, 'text'],
      ['Society agreement #', 14, 'text'],
      ...trailV3(v),
    ],
    ALT: (v) => [
      header(),
      ['Alternate title', 60, 'text'],
      ['Title type', 2, 'text', 'cwr_title_types'],
      ['Language code', 2, 'text'],
      ...trailV3(v),
    ],
    EWT: (v) => [
      header(),
      ['Entire work title', 60, 'text'],
      ['Entire ISWC', 11, 'iswc'],
      ['Language', 2, 'text'],
      ['Writer 1 last', 45, 'text'],
      ['Writer 1 first', 30, 'text'],
      ['Source', 60, 'text'],
      ...trailV3(v),
    ],
    VER: (v) => [
      header(),
      ['Original title', 60, 'text'],
      ['Original ISWC', 11, 'iswc'],
      ['Language', 2, 'text'],
      ['Writer 1 last', 45, 'text'],
      ['Writer 1 first', 30, 'text'],
      ...trailV3(v),
    ],
    PER: (v) => [
      header(),
      ['Performer last', 45, 'text'],
      ['Performer first', 30, 'text'],
      ['IPI name #', 11, 'ipi'],
      ...trailV3(v),
    ],
    REC: (v) => {
      const base = [
        header(),
        ['Release date', 8, 'date'],
        ['Reserved (legacy)', 60, 'text'],
        ['Release duration', 6, 'num'],
        ['Reserved', 5, 'text'],
        ['Album title', 60, 'text'],
        ['Album label', 60, 'text'],
        ['Release catalog #', 18, 'text'],
        ['EAN/UPC', 13, 'text'],
        ['ISRC', 12, 'text'],
        ['Recording format', 1, 'text'],
        ['Recording technique', 1, 'text', 'cwr_recording_techniques'],
        ['Media type', 3, 'text'],
      ];
      if (v === '2.2' || v === '3.0' || v === '3.1') {
        base.push(['Recording title', 60, 'text']);
        base.push(['Version title', 60, 'text']);
        base.push(['Display artist', 60, 'text']);
        base.push(['Record label', 60, 'text']);
        base.push(['ISRC validity', 1, 'text']);
        base.push(['Submitter recording ID', 14, 'text']);
      }
      return base;
    },
    ORN: (v) => [
      header(),
      ['Intended purpose', 3, 'text', 'cwr_intended_purposes'],
      ['Production title', 60, 'text'],
      ['CD identifier', 15, 'text'],
      ['Cut number', 4, 'text'],
      ['Library', 60, 'text'],
      ['BLTVR', 1, 'text'],
      ['VISAN', 25, 'text'],
      ['Production #', 12, 'text'],
      ['Episode title', 60, 'text'],
      ['Episode #', 20, 'text'],
      ['Production year', 4, 'text'],
      ['AVI society code', 3, 'text'],
      ['AV number', 15, 'text'],
      ...trailV3(v),
    ],
    INS: (v) => [
      header(),
      ['Number of voices', 3, 'text'],
      ['Standard instrumentation type', 3, 'text', 'cwr_standard_instrumentations'],
      ['Instrumentation description', 50, 'text'],
      ...trailV3(v),
    ],
    IND: (v) => [
      header(),
      ['Instrument code', 3, 'text', 'cwr_instruments'],
      ['Number of players', 3, 'num'],
      ...trailV3(v),
    ],
    COM: (v) => [
      header(),
      ['Component title', 60, 'text'],
      ['Component ISWC', 11, 'iswc'],
      ['Submitter work #', 14, 'text'],
      ['Duration', 6, 'num'],
      ['Writer 1 last', 45, 'text'],
      ['Writer 1 first', 30, 'text'],
      ...trailV3(v),
    ],
    MSG: (v) => [
      header(),
      ['Message type', 1, 'text'],
      ['Original record type', 3, 'text'],
      ['Message level', 1, 'text'],
      ['Validation #', 3, 'text'],
      ['Message text', 150, 'text'],
      ...trailV3(v),
    ],
    ARI: (v) => [
      header(),
      ['Society #', 3, 'text'],
      ['Work #', 14, 'text'],
      ['Type of right', 3, 'text', 'cwr_type_of_rights'],
      ['Subject code', 3, 'text', 'cwr_subject_codes'],
      ['Note', 80, 'text'],
      ...trailV3(v),
    ],
    XRF: () => [
      header(),
      ['Organisation code', 3, 'text'],
      ['Identifier', 14, 'text'],
      ['Identifier type', 1, 'text'],
      ['Validity indicator', 1, 'text'],
      ['Reserved', 2, 'text'],
    ],
    ACK: (v) => [
      header(),
      ['Creation date', 8, 'date'],
      ['Creation time', 6, 'time'],
      ['Original group ID', 5, 'text'],
      ['Original transaction seq', 8, 'text'],
      ['Original transaction type', 3, 'text', 'cwr_transaction_types'],
      ['Creation title', 60, 'text'],
      ['Submitter creation #', 20, 'text'],
      ['Recipient creation #', 20, 'text'],
      ['Processing date', 8, 'date'],
      ['Transaction status', 2, 'text', 'cwr_error_codes'],
      ...trailV3(v),
    ],
  };

  function header() { return ['__hdr', 19, 'header']; }
  function trailV3(v) {
    return (v === '3.0' || v === '3.1') ? [['Reserved', 2, 'text']] : [];
  }

  function nwrLike(v) {
    return [
      header(),
      ['Work title', 60, 'text'],
      ['Language', 2, 'text'],
      ['Submitter work #', 14, 'text'],
      ['ISWC', 11, 'iswc'],
      ['Copyright date', 8, 'date'],
      ['Copyright #', 12, 'text'],
      ['Musical work distribution category', 3, 'text', 'cwr_work_categories'],
      ['Duration', 6, 'num'],
      ['Recorded indicator', 1, 'flag'],
      ['Text-music relationship', 3, 'text', 'cwr_text_music_relationships'],
      ['Composite type', 3, 'text', 'cwr_composite_types'],
      ['Version type', 3, 'text', 'cwr_version_types'],
      ['Excerpt type', 3, 'text', 'cwr_composite_types'],
      ['Music arrangement', 3, 'text', 'music_arrangements'],
      ['Lyric adaptation', 3, 'text', 'lyric_adaptations'],
      ['Contact name', 30, 'text'],
      ['Contact ID', 10, 'text'],
      ['Work type', 2, 'text', 'cwr_work_categories'],
      ['Grand rights ind', 1, 'flag'],
      ['Composite component count', 3, 'num'],
      ['Date of publication of printed edition', 8, 'date'],
      ['Exceptional clause', 1, 'text'],
      ['Opus number', 25, 'text'],
      ['Catalogue #', 25, 'text'],
      ['Priority flag', 1, 'text'],
      ...trailV3(v),
    ];
  }
  function pubLike(v) {
    return [
      header(),
      ['Publisher sequence #', 2, 'num'],
      ['Interested party #', 9, 'text'],
      ['Publisher name', 45, 'text'],
      ['Publisher unknown', 1, 'flag'],
      ['Publisher type', 2, 'text', 'cwr_writer_designations'],
      ['Tax ID #', 9, 'text'],
      ['IPI name #', 11, 'ipi'],
      ['Submitter agreement #', 14, 'text'],
      ['PR society', 3, 'text'],
      ['PR ownership share', 5, 'num'],
      ['MR society', 3, 'text'],
      ['MR ownership share', 5, 'num'],
      ['SR society', 3, 'text'],
      ['SR ownership share', 5, 'num'],
      ['Special agreements', 1, 'text'],
      ['First recording refusal', 1, 'flag'],
      ['Reserved (USA license ind)', 1, 'text'],
      ['PR affiliation society', 3, 'num'],
      ['IPI base #', 13, 'text'],
      ['Agreement type', 2, 'text', 'cwr_agreement_types'],
      ['Sales/Manufacture clause', 1, 'text'],
      ['Share in other territories', 5, 'num'],
      ['Agreement #', 14, 'num'],
      ['Number of shares', 9, 'text'],
      ...trailV3(v),
    ];
  }
  function terLike(v) {
    return [
      header(),
      ['Interested party #', 9, 'text'],
      ['Constant', 6, 'text'],
      ['PR collection share', 5, 'num'],
      ['MR collection share', 5, 'num'],
      ['SR collection share', 5, 'num'],
      ['Inclusion/exclusion', 1, 'text'],
      ['TIS numeric code', 4, 'num'],
      ['Shares change', 1, 'text'],
      ['Sequence #', 3, 'num'],
      ...trailV3(v),
    ];
  }
  function wrLike(v) {
    return [
      header(),
      ['Interested party #', 9, 'text'],
      ['Writer last name', 45, 'text'],
      ['Writer first name', 30, 'text'],
      ['Writer unknown', 1, 'flag'],
      ['Writer designation', 2, 'text', 'cwr_writer_designations'],
      ['Tax ID #', 9, 'text'],
      ['Personal #', 12, 'text'],
      ['IPI name #', 11, 'ipi'],
      ['PR society', 3, 'text'],
      ['PR share', 5, 'num'],
      ['MR society', 3, 'text'],
      ['MR share', 5, 'num'],
      ['SR society', 3, 'text'],
      ['SR share', 5, 'num'],
      ['Reversionary indicator', 1, 'text'],
      ['First recording refusal', 1, 'flag'],
      ['Work for hire', 1, 'text'],
      ['IPI base #', 13, 'text'],
      ['USA license ind', 1, 'text'],
      ...trailV3(v),
    ];
  }
  function wtLike(v) {
    return [
      header(),
      ['Interested party #', 9, 'text'],
      ['PR share', 5, 'num'],
      ['MR share', 5, 'num'],
      ['SR share', 5, 'num'],
      ['Inclusion/exclusion', 1, 'text'],
      ['TIS numeric code', 4, 'num'],
      ['Shares change', 1, 'text'],
      ['Sequence #', 3, 'num'],
      ...trailV3(v),
    ];
  }

  function fieldsFor(recType, version) {
    const fn = SCHEMA[recType];
    if (!fn) return null;
    return fn(version);
  }

  function decodeLine(line, version) {
    if (!line || line.length < 3) return null;
    const recType = line.slice(0, 3);
    const fields = fieldsFor(recType, version);
    if (!fields) {
      return { recType, fields: [{ name: 'Raw', value: line, kind: 'text' }] };
    }
    let pos = 0;
    const out = [];
    for (const [name, len, kind, lookup] of fields) {
      if (name === '__hdr') {
        // Standard header: record type (3) + txn seq (8) + rec seq (8)
        out.push({ name: 'Record type', value: line.slice(0, 3).trim(), kind: 'text' });
        out.push({ name: 'Transaction seq #', value: line.slice(3, 11).trim(), kind: 'num' });
        out.push({ name: 'Record seq #', value: line.slice(11, 19).trim(), kind: 'num' });
        pos = 19;
        continue;
      }
      const raw = line.slice(pos, pos + len);
      pos += len;
      out.push({ name, value: raw, kind, lookup, raw });
    }
    return { recType, version, fields: out };
  }

  // Codes lookup using ref-data (window.REF.raw[table])
  function lookupCode(table, code) {
    const REF = window.REF;
    if (!code || !REF || !REF.ready || !REF.raw) return null;
    const arr = REF.raw[table];
    if (!Array.isArray(arr)) return null;
    const trimmed = String(code).trim();
    return arr.find((x) => x.code === trimmed) || null;
  }

  window.CwrDecode = { fieldsFor, decodeLine, lookupCode, SCHEMA };
})();
