// parsers-v5.5.93-3.js — EXACT extraction of steg_id parsing logic from
// the shipped v5.5.93-3 extension (both Firefox and Chrome — identical).
//
// These regexes and branches are lifted VERBATIM from background.js:
//   - Firefox: /tmp/ext-stage-firefox/background.js lines 240-241, 1606-1610,
//     1727-1803, 1965-1969, 2131-2135
//   - Chrome:  /tmp/ext-stage-chrome/background.js   (same lines, identical)
//
// The harness replays each of these parse sites against canned LSB/SEI/DCT
// payloads to prove the v5.5.93-3 code fails on canonical 32-hex steg_id
// per the /sign/init wire contract (routers/sign.py line 83).

'use strict';

// ─── regexes — VERBATIM from background.js line 240-241 ─────────────────────
const _UUID_RE  = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const _HEX64_RE = /^[0-9a-f]{64}$/i;

// ─── parse logic replays — VERBATIM branches from background.js ─────────────

/** Layer 1 image LSB parse — line 1964-1971 of background.js.
 *  Called with the raw bytes recovered from LSB extraction.
 *  Returns {stegId, extractedCreatorId, preSignHash, stegSource} or null.
 */
function parseImageLSB(payloadBytes) {
  if (!payloadBytes) return null;
  const parts = new TextDecoder().decode(payloadBytes).split('|');
  if (_UUID_RE.test(parts[0])) {
    const stegId = parts[0];
    const extractedCreatorId = (parts[1] && _UUID_RE.test(parts[1])) ? parts[1] : null;
    const preSignHash = (parts[2] && _HEX64_RE.test(parts[2])) ? parts[2] : null;
    return { stegId, extractedCreatorId, preSignHash, stegSource: 'lsb' };
  }
  return null;
}

/** Video keyframe DCT parse — line 1605-1614 of background.js. */
function parseKeyframeDCT(dctPayloadBytes) {
  if (!dctPayloadBytes) return null;
  const parts = new TextDecoder().decode(dctPayloadBytes).split('|');
  if (parts.length >= 1 && _UUID_RE.test(parts[0])) {
    return {
      stegId: parts[0],
      preSignHash: (parts[2] && _HEX64_RE.test(parts[2])) ? parts[2] : null,
    };
  }
  return null;
}

/** Video Layer 2 SEI NAL parse — line 1736-1744 of background.js.
 *  Input is a string (already decoded by _extractSEINALFromMP4). */
function parseSEINAL(seiPayloadString) {
  if (!seiPayloadString) return null;
  const parts = seiPayloadString.split('|');
  if (parts.length >= 1 && _UUID_RE.test(parts[0])) {
    return {
      stegId: parts[0],
      preSignHash: (parts[1] && _HEX64_RE.test(parts[1])) ? parts[1] : null,
    };
  }
  return null;
}

/** Video Layer 3 audio LSB parse — line 1755-1763 of background.js. */
function parseAudioLSB(audioPayloadString) {
  if (!audioPayloadString) return null;
  const parts = audioPayloadString.split('|');
  if (parts.length >= 1 && _UUID_RE.test(parts[0])) {
    return {
      stegId: parts[0],
      preSignHash: (parts[1] && _HEX64_RE.test(parts[1])) ? parts[1] : null,
    };
  }
  return null;
}

/** Video DCT LSB (second) — line 2129-2136 of background.js. */
function parseVideoDCTLsb(payloadBytes) {
  if (!payloadBytes) return null;
  const parts = new TextDecoder().decode(payloadBytes).split('|');
  if (_UUID_RE.test(parts[0])) {
    return {
      stegId: parts[0],
      dctCreatorId: (parts[1] && _UUID_RE.test(parts[1])) ? parts[1] : null,
      dctPreSignHash: (parts[2] && _HEX64_RE.test(parts[2])) ? parts[2] : null,
    };
  }
  return null;
}

/** MP4 udta/meta stegId check — line 1727-1729 / 1733+ (gates Layers 2-5).
 *  Input: a candidate stegId string read from MYSTERION_STEG_ID metadata. */
function acceptMetaStegId(stegId) {
  return !!(stegId && _UUID_RE.test(stegId));
}

// ─── Verdict Table Pathways — verify dispatcher ─────────────────────────────
//
// Simulates the extension's decision tree:
//   Extract → got stegId? → POST /verify → map response → terminal
//   Extract → no stegId  → pHash fallback → terminal 6/7/8
//
// Returns {verdict, terminal, stegId?, stegSource?, sentToServer?}.
// 'sentToServer' is the request body the extension would send.
//
// Terminals (from the Verdict Table Pathways doc):
//   1  AUTHENTIC                — verified, pHash<=T, hash OK
//   2  ALTERED                  — verified=false with steg_id
//   3  RESIGNED                 — pHash<=T, different steg_id
//   4  CREATOR_MISMATCH         — creator_id != expected
//   5  TRANSFORMED               — pHash<=T but hash mismatch
//   6  UNKNOWN_SIMILAR_TO_ALT   — pHash close to an altered
//   7  UNKNOWN_SIMILAR_TO_OK    — pHash close to an authentic
//   8  UNKNOWN                  — nothing matches
//   9  FIRED_ALERT              — altered by different creator → fire alert

function runVerify({ lsbPayload, pHash, fileBlake3, meta }, serverMock) {
  // ── Stage 1: fast-path hash lookup (unchanged) ──────────────────────────
  const fast = serverMock.fastPath(fileBlake3);
  if (fast && fast.found && fast.steg_id) {
    const verifyResp = serverMock.verify({
      steg_id: fast.steg_id,
      phash: pHash,
      content_hash: fileBlake3,
    });
    return toVerdict(verifyResp, 'blake3-fast', fast.steg_id);
  }

  // ── Stage 2: try to extract steg_id ─────────────────────────────────────
  let stegId = null, stegSource = null, extractedCreatorId = null;

  // 2a: metadata box (gated by acceptMetaStegId)
  if (meta && meta.MYSTERION_STEG_ID && acceptMetaStegId(meta.MYSTERION_STEG_ID)) {
    stegId = meta.MYSTERION_STEG_ID;
    stegSource = 'meta';
  }

  // 2b: LSB payload
  if (!stegId) {
    const r = parseImageLSB(lsbPayload);
    if (r) {
      stegId = r.stegId;
      stegSource = r.stegSource;
      extractedCreatorId = r.extractedCreatorId;
    }
  }

  // 2c: if still none → pHash fallback
  if (!stegId) {
    const p = serverMock.pHashLookup(pHash);
    if (!p) return { verdict: 'unknown', terminal: 8 };
    if (p.kind === 'altered')    return { verdict: 'unknown', terminal: 6, close: p };
    if (p.kind === 'authentic')  return { verdict: 'unknown', terminal: 7, close: p };
    return { verdict: 'unknown', terminal: 8 };
  }

  // ── Stage 3: verify with extracted stegId ───────────────────────────────
  const verifyResp = serverMock.verify({
    steg_id: stegId,
    phash: pHash,
    content_hash: fileBlake3,
    extracted_creator_id: extractedCreatorId,
  });
  return toVerdict(verifyResp, stegSource, stegId);
}

function toVerdict(resp, stegSource, stegId) {
  if (!resp) return { verdict: 'unknown', terminal: 8, stegId, stegSource };
  if (resp.verdict === 'authentic')
    return { verdict: 'authentic', terminal: 1, stegId, stegSource };
  if (resp.verdict === 'altered') {
    // Fired alert if different-creator altered
    if (resp.creator_mismatch)
      return { verdict: 'altered', terminal: 9, stegId, stegSource, alertFired: true };
    return { verdict: 'altered', terminal: 2, stegId, stegSource };
  }
  if (resp.verdict === 'resigned')
    return { verdict: 'resigned', terminal: 3, stegId, stegSource };
  if (resp.verdict === 'creator_mismatch')
    return { verdict: 'creator_mismatch', terminal: 4, stegId, stegSource };
  if (resp.verdict === 'transformed')
    return { verdict: 'transformed', terminal: 5, stegId, stegSource };
  return { verdict: 'unknown', terminal: 8, stegId, stegSource };
}

module.exports = {
  _UUID_RE, _HEX64_RE,
  parseImageLSB, parseKeyframeDCT, parseSEINAL, parseAudioLSB,
  parseVideoDCTLsb, acceptMetaStegId,
  runVerify,
};
