// ============================================================================
// CWR TEST-ENVIRONMENT SUBMISSION FLOW
//
// Each society publishes a TEST environment endpoint where you submit CWR
// files and receive synthetic ACKs WITHOUT real money/registrations. This is
// the loop you run to certify before going live.
//
// What this module does:
//   1. Hold per-society test-env config (endpoint, credentials placeholders,
//      file naming convention, expected ACK timing).
//   2. Drive a submission cycle: build test file → byte-diff audit → ID
//      validation → submit → poll for ACK → reconcile.
//   3. Track each cycle in a run-log so you can see "we submitted 47 test
//      files to ASCAP, 47 returned AS, 0 RJ — we're certified for v3.1 NWR".
//   4. Surface a single "Run cycle" UI that walks an operator through it.
//
// THIS IS PROTOTYPE INFRASTRUCTURE: real submission needs network calls
// + real credentials. We simulate the network leg deterministically so the
// rest of the pipeline (build → audit → submit → ACK → reconcile) is fully
// exercised.
//
// EXPORT: window.CwrTestEnv = { CONFIGS, runCycle, runFullCertification, ... }
// ============================================================================
(function () {

  // ─── Per-society test-env configs ──────────────────────────────────────
  // Endpoints are public TEST URLs (not production); credentials are placeholders.
  const CONFIGS = {
    ASCAP: {
      society: 'ASCAP',
      cisac: '010',
      env: 'TEST',
      transport: 'sftp',
      host: 'sftp-test.ascap.com',
      port: 22,
      directory: '/incoming/test',
      filenamePattern: 'CW{YY}{seq}{sender}_010.V{vv}',
      ackPath: '/outgoing/test',
      ackTiming: '2-4 business days (test env: 5-15 mins synthetic)',
      contact: 'cwr-support@ascap.com',
      versionsAccepted: ['2.1r7', '2.2', '3.0', '3.1'],
      certificationProcess: 'Submit 5 NWR + 1 REV + 1 ISW. All must AS. Then live cutover.',
    },
    BMI: {
      society: 'BMI',
      cisac: '021',
      env: 'TEST',
      transport: 'sftp',
      host: 'sftp-test.bmi.com',
      port: 22,
      directory: '/test/inbound',
      filenamePattern: 'CW{YY}{seq}{sender}_021.V{vv}',
      ackPath: '/test/outbound',
      ackTiming: '1-3 business days',
      contact: 'cwrsubmissions@bmi.com',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'Pilot batch of 10 works; BMI staff manually reviews first 3.',
    },
    PRS: {
      society: 'PRS',
      cisac: '052',
      env: 'TEST',
      transport: 'portal',
      host: 'cwr-test.prsformusic.com',
      directory: 'upload',
      filenamePattern: 'CW{YY}{seq}{sender}_052.V{vv}',
      ackPath: 'download',
      ackTiming: '1-2 business days',
      contact: 'wid@prsformusic.com',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'CISAC EBU exchange. PRS validates against CWR validator + PRS-specific rules.',
    },
    GEMA: {
      society: 'GEMA',
      cisac: '035',
      env: 'TEST',
      transport: 'sftp',
      host: 'cwr-test.gema.de',
      port: 22,
      directory: '/in',
      filenamePattern: 'CW{YY}{seq}{sender}_035.V{vv}',
      ackPath: '/out',
      ackTiming: '3-5 business days',
      contact: 'documentation@gema.de',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'GEMA Test → Validation report → fix → re-test until clean. Avg 3 cycles.',
    },
    SACEM: {
      society: 'SACEM',
      cisac: '058',
      env: 'TEST',
      transport: 'portal',
      host: 'cwr-test.sacem.fr',
      directory: 'depot',
      filenamePattern: 'CW{YY}{seq}{sender}_058.V{vv}',
      ackPath: 'retrait',
      ackTiming: '5-10 business days',
      contact: 'cwr@sacem.fr',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'SACEM validation portal. Strict on writer designations and IPI presence.',
    },
    JASRAC: {
      society: 'JASRAC',
      cisac: '101',
      env: 'TEST',
      transport: 'sftp',
      host: 'cwr-test.jasrac.or.jp',
      port: 22,
      directory: '/inbound',
      filenamePattern: 'CW{YY}{seq}{sender}_101.V{vv}',
      ackPath: '/outbound',
      ackTiming: '5-7 business days',
      contact: 'international@jasrac.or.jp',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'Strict NCT/NPN/NWN companion records for non-Roman titles. Test pack: 3 ASCII + 3 Japanese.',
    },
    SESAC: {
      society: 'SESAC', cisac: '071', env: 'TEST', transport: 'sftp',
      host: 'sftp-test.sesac.com', port: 22, directory: '/cwr/test',
      filenamePattern: 'CW{YY}{seq}{sender}_071.V{vv}', ackPath: '/cwr/test-ack',
      ackTiming: '2-3 business days', contact: 'workreg@sesac.com',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'Coordinated through SESAC publisher relations. Pilot of 5 works.',
    },
    GMR: {
      society: 'GMR', cisac: '170', env: 'TEST', transport: 'portal',
      host: 'portal-test.globalmusicrights.com', directory: 'cwr-upload',
      filenamePattern: 'CW{YY}{seq}{sender}_170.V{vv}', ackPath: 'cwr-ack',
      ackTiming: '2-4 business days', contact: 'support@globalmusicrights.com',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'Manual review of first batch by GMR team.',
    },
    SOCAN: {
      society: 'SOCAN', cisac: '055', env: 'TEST', transport: 'sftp',
      host: 'sftp-test.socan.com', port: 22, directory: '/test/in',
      filenamePattern: 'CW{YY}{seq}{sender}_055.V{vv}', ackPath: '/test/out',
      ackTiming: '2-4 business days', contact: 'works@socan.com',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'SOCAN validates IPI presence on all writers and publisher type codes.',
    },
    APRA_AMCOS: {
      society: 'APRA AMCOS', cisac: '040', env: 'TEST', transport: 'sftp',
      host: 'cwr-test.apraamcos.com.au', port: 22, directory: '/in',
      filenamePattern: 'CW{YY}{seq}{sender}_040.V{vv}', ackPath: '/out',
      ackTiming: '3-5 business days', contact: 'cwr@apraamcos.com.au',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'APRA validates writer IPI numbers against IPI database.',
    },
    STIM: {
      society: 'STIM', cisac: '079', env: 'TEST', transport: 'sftp',
      host: 'cwr-test.stim.se', port: 22, directory: '/upload',
      filenamePattern: 'CW{YY}{seq}{sender}_079.V{vv}', ackPath: '/download',
      ackTiming: '3-5 business days', contact: 'works@stim.se',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'STIM uses CISAC validator + Nordic CWR rules.',
    },
    SIAE: {
      society: 'SIAE', cisac: '062', env: 'TEST', transport: 'sftp',
      host: 'cwr-test.siae.it', port: 22, directory: '/incoming',
      filenamePattern: 'CW{YY}{seq}{sender}_062.V{vv}', ackPath: '/outgoing',
      ackTiming: '5-7 business days', contact: 'edi@siae.it',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'SIAE strict on writer designation and Italian author society codes.',
    },
    AKM: {
      society: 'AKM', cisac: '005', env: 'TEST', transport: 'sftp',
      host: 'cwr-test.akm.at', port: 22, directory: '/in',
      filenamePattern: 'CW{YY}{seq}{sender}_005.V{vv}', ackPath: '/out',
      ackTiming: '3-5 business days', contact: 'cwr@akm.at',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'AKM uses CISAC validator + Austrian language rules for German titles.',
    },
    MLC: {
      society: 'MLC', cisac: '775', env: 'TEST', transport: 'portal',
      host: 'cwr-test.themlc.com', directory: 'submissions',
      filenamePattern: 'CW{YY}{seq}{sender}_775.V{vv}', ackPath: 'acks',
      ackTiming: '1-2 business days (faster than PROs)', contact: 'support@themlc.com',
      versionsAccepted: ['3.0', '3.1'],
      certificationProcess: 'MLC focuses on MR shares; PR can be 0. Tight on ISRC linkage.',
    },
    HFA: {
      society: 'HFA', cisac: '999', env: 'TEST', transport: 'portal',
      host: 'songfile-test.harryfox.com', directory: 'cwr-upload',
      filenamePattern: 'CW{YY}{seq}{sender}_999.V{vv}', ackPath: 'cwr-ack',
      ackTiming: '1-3 business days', contact: 'cwr@harryfox.com',
      versionsAccepted: ['2.2', '3.0', '3.1'],
      certificationProcess: 'HFA emphasises mechanical licensing data; uses subset of CWR.',
    },
    SAYCE: {
      society: 'SAYCE', cisac: '095', env: 'TEST', transport: 'sftp',
      host: 'cwr-test.sayce.com.ec', port: 22, directory: '/in',
      filenamePattern: 'CW{YY}{seq}{sender}_095.V{vv}', ackPath: '/out',
      ackTiming: '5-10 business days', contact: 'cwr@sayce.com.ec',
      versionsAccepted: ['2.2', '3.0'],
      certificationProcess: 'SAYCE accepts CWR via CISAC EBU exchange; standard validation.',
    },
  };

  // ─── Cycle steps ────────────────────────────────────────────────────────
  // 1. BUILD       — generate test transmission (NWR for that society)
  // 2. AUDIT       — run byte-diff against society layout
  // 3. ID-VALIDATE — run identifier check-digit verification
  // 4. SUBMIT      — simulate transport (sftp/portal) — deterministic outcome
  // 5. ACK-WAIT    — synthesise an ACK based on audit outcome
  // 6. RECONCILE   — match ACK to submitted works, mark cert progress

  function buildSubmissionFile(society, version) {
    // Use the byte-diff reference build — it's the most rigorous test pack
    if (!window.CwrByteDiff) return null;
    const lines = window.CwrByteDiff.buildReferenceTransmission(society, version);
    if (!lines) return null;
    return {
      society, version,
      lines,
      content: lines.join('\r\n') + '\r\n',
      bytes: lines.reduce((s, l) => s + l.length + 2, 0),
      records: lines.length,
    };
  }

  function genFilename(society, version, seq, senderId) {
    const cfg = CONFIGS[society];
    if (!cfg) return null;
    const yy = String(new Date().getFullYear()).slice(2);
    const seqStr = String(seq).padStart(4, '0');
    const senderStr = String(senderId).slice(-3).padStart(3, '0');
    const vv = version.replace('.', '').slice(0, 2);
    return cfg.filenamePattern
      .replace('{YY}', yy)
      .replace('{seq}', seqStr)
      .replace('{sender}', senderStr)
      .replace('{vv}', vv);
  }

  function runCycle(society, version, opts) {
    opts = opts || {};
    const cfg = CONFIGS[society];
    if (!cfg) return { error: 'No test-env config for ' + society };

    const cycle = {
      society, version,
      cycleId: 'CYC-' + Date.now() + '-' + Math.random().toString(36).slice(2, 6).toUpperCase(),
      startedAt: new Date().toISOString(),
      steps: [],
      status: 'running',
    };

    // Step 1: BUILD
    let t = performance.now();
    const file = buildSubmissionFile(society, version);
    cycle.steps.push({
      step: 'build', ok: !!file,
      durationMs: +(performance.now() - t).toFixed(2),
      detail: file ? (file.records + ' records · ' + file.bytes + ' bytes') : 'failed to build',
    });
    if (!file) { cycle.status = 'failed'; return cycle; }

    cycle.filename = genFilename(society, version, opts.seq || 1, file.lines[0].slice(5, 14));
    cycle.fileBytes = file.bytes;
    cycle.fileRecords = file.records;

    // Step 2: AUDIT (byte-diff)
    t = performance.now();
    const audit = window.CwrByteDiff.auditTransmission(file.lines, society, version);
    cycle.steps.push({
      step: 'byte-diff audit', ok: audit.ok,
      durationMs: +(performance.now() - t).toFixed(2),
      detail: audit.checksPass + '/' + audit.checksTotal + ' checks · ' + audit.issues.length + ' issues',
      issues: audit.issues,
    });

    // Step 3: ID-VALIDATE
    t = performance.now();
    let idAudit = { issues: [], stats: {}, ok: true };
    if (window.CwrIdValidate) {
      idAudit = window.CwrIdValidate.auditTransmission(file.lines, version);
    }
    cycle.steps.push({
      step: 'identifier validation', ok: idAudit.ok,
      durationMs: +(performance.now() - t).toFixed(2),
      detail: idAudit.summary || (idAudit.issues.length + ' ID issues'),
      issues: idAudit.issues,
    });

    // Step 4: SUBMIT (simulated)
    t = performance.now();
    const submitOk = audit.ok && idAudit.ok;
    cycle.steps.push({
      step: 'submit (' + cfg.transport + ' to ' + cfg.host + ')',
      ok: true, // submission itself always succeeds in sim; ACK reflects content
      durationMs: +(performance.now() - t).toFixed(2) + Math.random() * 30,
      detail: 'queued · ' + cycle.filename,
    });

    // Step 5: ACK-WAIT (synthetic)
    t = performance.now();
    // ACK type: AS=accepted, RJ=rejected, NP=not processed, AR=accepted-with-warning
    let ackType, ackReason;
    if (audit.ok && idAudit.ok) {
      ackType = 'AS';
      ackReason = 'All works accepted by ' + society + ' test environment';
    } else if (audit.issues.some(i => i.severity === 'high')) {
      ackType = 'RJ';
      ackReason = 'Field-layout violation: ' + audit.issues[0].message;
    } else if (idAudit.issues.length > 0) {
      ackType = 'AR';
      ackReason = 'Accepted with warnings: ' + idAudit.issues[0].message;
    } else {
      ackType = 'AS';
      ackReason = 'Accepted';
    }
    cycle.steps.push({
      step: 'ACK received',
      ok: ackType === 'AS' || ackType === 'AR',
      durationMs: 200 + Math.random() * 300, // simulate 200-500ms test-env latency
      detail: 'ACK type: ' + ackType + ' · ' + ackReason,
      ack: { type: ackType, reason: ackReason, receivedAt: new Date().toISOString() },
    });

    // Step 6: RECONCILE
    cycle.steps.push({
      step: 'reconcile to catalog',
      ok: true,
      durationMs: 5,
      detail: ackType === 'AS' ? 'Marked ' + file.records + ' records as test-accepted' :
              ackType === 'AR' ? 'Accepted with ' + idAudit.issues.length + ' warnings to address' :
              'Recorded rejection; queued for fix-and-resubmit',
    });

    cycle.endedAt = new Date().toISOString();
    cycle.totalMs = cycle.steps.reduce((s, x) => s + (x.durationMs || 0), 0);
    cycle.ackType = ackType;
    cycle.ackReason = ackReason;
    cycle.status = ackType === 'AS' ? 'accepted' : ackType === 'AR' ? 'accepted-with-warnings' : 'rejected';
    cycle.passedCertGate = ackType === 'AS';

    return cycle;
  }

  function runFullCertification(version) {
    version = version || '3.1';
    const societies = Object.keys(CONFIGS);
    const cycles = societies.map(soc => runCycle(soc, version));
    const passed = cycles.filter(c => c.passedCertGate).length;
    return {
      version,
      societiesCertified: passed,
      societiesTotal: societies.length,
      cycles,
      certified: passed === societies.length,
      summary: passed + '/' + societies.length + ' societies passed cert gate at v' + version,
    };
  }

  function runMatrix() {
    // Run all societies × supported versions
    const matrix = [];
    for (const soc of Object.keys(CONFIGS)) {
      const cfg = CONFIGS[soc];
      for (const v of cfg.versionsAccepted) {
        matrix.push(runCycle(soc, v));
      }
    }
    return {
      total: matrix.length,
      accepted: matrix.filter(c => c.status === 'accepted').length,
      withWarnings: matrix.filter(c => c.status === 'accepted-with-warnings').length,
      rejected: matrix.filter(c => c.status === 'rejected').length,
      cycles: matrix,
    };
  }

  window.CwrTestEnv = {
    CONFIGS,
    buildSubmissionFile,
    genFilename,
    runCycle,
    runFullCertification,
    runMatrix,
  };
})();
