// parsers-v5.5.93-4.js — FIXED parsers.
//
// Exactly the same structure as parsers-v5.5.93-3.js, but with a steg_id
// normalizer that accepts EITHER:
//   - canonical 32-hex-lowercase (the wire format per routers/sign.py:83)
//   - legacy 36-dashed UUID       (pre-v5.5.93 compat)
// and normalizes to canonical 32-hex lowercase before use.
//
// Matches the SEI extractor fix in v5.5.93 re-spin #7 (release notes line 111).

'use strict';

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;
const _HEX32_RE = /^[0-9a-f]{32}$/i;

/** Normalize a candidate steg_id to canonical 32-hex lowercase.
 *  Accepts either 32-hex or dashed UUID. Returns null on mismatch. */
function _normalizeStegId(s) {
  if (!s || typeof s !== 'string') return null;
  if (_HEX32_RE.test(s)) return s.toLowerCase();
  if (_UUID_RE.test(s))  return s.replace(/-/g, '').toLowerCase();
  return null;
}

// ─── Replays of each parse-site, upgraded to use _normalizeStegId ───────────

function parseImageLSB(payloadBytes) {
  if (!payloadBytes) return null;
  const parts = new TextDecoder().decode(payloadBytes).split('|');
  const stegId = _normalizeStegId(parts[0]);
  if (!stegId) return null;
  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' };
}

function parseKeyframeDCT(dctPayloadBytes) {
  if (!dctPayloadBytes) return null;
  const parts = new TextDecoder().decode(dctPayloadBytes).split('|');
  const stegId = _normalizeStegId(parts[0]);
  if (!stegId) return null;
  return {
    stegId,
    preSignHash: (parts[2] && _HEX64_RE.test(parts[2])) ? parts[2] : null,
  };
}

function parseSEINAL(seiPayloadString) {
  if (!seiPayloadString) return null;
  const parts = seiPayloadString.split('|');
  const stegId = _normalizeStegId(parts[0]);
  if (!stegId) return null;
  return {
    stegId,
    preSignHash: (parts[1] && _HEX64_RE.test(parts[1])) ? parts[1] : null,
  };
}

function parseAudioLSB(audioPayloadString) {
  if (!audioPayloadString) return null;
  const parts = audioPayloadString.split('|');
  const stegId = _normalizeStegId(parts[0]);
  if (!stegId) return null;
  return {
    stegId,
    preSignHash: (parts[1] && _HEX64_RE.test(parts[1])) ? parts[1] : null,
  };
}

function parseVideoDCTLsb(payloadBytes) {
  if (!payloadBytes) return null;
  const parts = new TextDecoder().decode(payloadBytes).split('|');
  const stegId = _normalizeStegId(parts[0]);
  if (!stegId) return null;
  return {
    stegId,
    dctCreatorId: (parts[1] && _UUID_RE.test(parts[1])) ? parts[1] : null,
    dctPreSignHash: (parts[2] && _HEX64_RE.test(parts[2])) ? parts[2] : null,
  };
}

function acceptMetaStegId(stegId) {
  return _normalizeStegId(stegId) !== null;
}

function runVerify({ lsbPayload, pHash, fileBlake3, meta }, serverMock) {
  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);
  }

  let stegId = null, stegSource = null, extractedCreatorId = null;

  if (meta && meta.MYSTERION_STEG_ID) {
    const norm = _normalizeStegId(meta.MYSTERION_STEG_ID);
    if (norm) { stegId = norm; stegSource = 'meta'; }
  }

  if (!stegId) {
    const r = parseImageLSB(lsbPayload);
    if (r) {
      stegId = r.stegId;
      stegSource = r.stegSource;
      extractedCreatorId = r.extractedCreatorId;
    }
  }

  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 };
  }

  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') {
    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, _HEX32_RE, _normalizeStegId,
  parseImageLSB, parseKeyframeDCT, parseSEINAL, parseAudioLSB,
  parseVideoDCTLsb, acceptMetaStegId,
  runVerify,
};
