/**
 * AS/PLATFORM/2026/003 T006 — goAML live-submission connector proof (mock-tested).
 *
 * Drives the REAL connector (server/lib/fincrime/goaml-connector.submitGoamlFormB) against
 * a LOCAL mock HTTP endpoint. NOT live-FIA. Every leg is Rule-18 paired (a positive bound to
 * its discriminating negative) so no pass is hollow-green:
 *
 *   FENCE       : unconfigured → throws GoamlNotConfiguredError  |  configured(env) → proceeds
 *   PAYLOAD     : complete → transmitted + well-formed at mock   |  incomplete → refused pre-send
 *   ACK MAPPING : 2xx+ref → FILED ; 2xx+ref+ack → ACKNOWLEDGED   |  2xx no-ref → REJECTED (no fab) ; non-2xx → REJECTED
 *   AUTH        : bearer configured → mock sees Authorization     |  none → mock sees NO Authorization
 *   TRANSPORT   : timeout/refused → TRANSPORT_ERROR (no fab ack)
 *
 * Self-contained: ephemeral localhost mock, no DB, no existing-state mutation (Rule 11).
 * The connector performs no DB write, so the prod NULL-ack fence is untouched. Exit 0 iff ALL pass.
 */

import { createServer, type Server, type IncomingMessage, type ServerResponse } from "http";
import { type AddressInfo } from "net";
import { ProofRun, verdict } from "./lib/proof-harness";
import { renderGoamlFormB, getGoamlDeploymentConfig, type FormBSource } from "../server/lib/fincrime/goaml-formb";
import {
  submitGoamlFormB,
  isGoamlConnectorConfigured,
  GoamlNotConfiguredError,
  GoamlIncompletePayloadError,
  type GoamlConnectorConfig,
} from "../server/lib/fincrime/goaml-connector";

// ── mock endpoint ────────────────────────────────────────────────────────────────
interface CapturedRequest {
  headers: Record<string, string | string[] | undefined>;
  bodyRaw: string;
  body: unknown;
}
let captured: CapturedRequest | null = null;
type MockResponder = (req: CapturedRequest) => { status: number; body: string; delayMs?: number };
let responder: MockResponder = () => ({ status: 200, body: "{}" });

function startMock(): Promise<{ server: Server; url: string }> {
  return new Promise((resolve) => {
    const server = createServer((req: IncomingMessage, res: ServerResponse) => {
      const chunks: Buffer[] = [];
      req.on("data", (c) => chunks.push(c as Buffer));
      req.on("end", () => {
        const bodyRaw = Buffer.concat(chunks).toString("utf8");
        let body: unknown = null;
        try { body = JSON.parse(bodyRaw); } catch { body = null; }
        captured = { headers: req.headers, bodyRaw, body };
        const r = responder(captured);
        const send = () => {
          res.writeHead(r.status, { "Content-Type": "application/json" });
          res.end(r.body);
        };
        if (r.delayMs && r.delayMs > 0) setTimeout(send, r.delayMs);
        else send();
      });
    });
    server.listen(0, "127.0.0.1", () => {
      const port = (server.address() as AddressInfo).port;
      resolve({ server, url: `http://127.0.0.1:${port}/goaml/submit` });
    });
  });
}

// ── synthetic (no-PII, opaque) rendered Form-B payload ─────────────────────────────
function makePayload() {
  const source: FormBSource = {
    subjectRef: "OPAQUE-CONNECTOR-PROOF-REF",
    subjectRefKind: "OPAQUE_EXTERNAL_REF",
    sourceType: "KYT_ALERT",
    disposition: "STR_RECOMMENDED",
    dispositionedAt: new Date("2026-06-16T00:00:00.000Z"),
    strFiled: false,
    strReportId: "str-connector-proof",
    strStatus: "PREPARED",
    narrative: "Synthetic suspicious-activity narrative — connector transport proof only.",
    strCreatedAt: new Date("2026-06-15T00:00:00.000Z"),
    fiaReference: null,
    filedAt: null,
    acknowledgedAt: null,
    preparedByRole: "risk_manager",
    approverRole: "admin",
    approvedAt: new Date("2026-06-16T00:00:00.000Z"),
  };
  return renderGoamlFormB(source, getGoamlDeploymentConfig());
}

function cfg(url: string, over: Partial<GoamlConnectorConfig> = {}): GoamlConnectorConfig {
  return { endpointUrl: url, authType: "none", authToken: null, timeoutMs: 5000, ...over };
}

async function main(): Promise<void> {
  const run = new ProofRun("AS/PLATFORM/2026/003 T006 — goAML connector (mock)", "DEV");
  const { server, url } = await startMock();
  const payload = makePayload();

  try {
    // ── FENCE (paired) ──────────────────────────────────────────────────────────
    // negative leg: no env endpoint → not configured AND submit refuses (the prod fence).
    delete process.env.GOAML_ENDPOINT_URL;
    delete process.env.GOAML_AUTH_TYPE;
    delete process.env.GOAML_AUTH_TOKEN;
    const cfgFalse = isGoamlConnectorConfigured();
    let fenced = false;
    try {
      await submitGoamlFormB(payload); // reads env → must throw
    } catch (e) {
      fenced = e instanceof GoamlNotConfiguredError;
    }
    run.leg({
      id: "T006-FENCE-NEG", scenario: "unconfigured (no GOAML_ENDPOINT_URL) → submission fenced",
      expectation: "isConfigured=false AND submit throws GoamlNotConfiguredError",
      observed: `isConfigured=${cfgFalse}; threw GoamlNotConfiguredError=${fenced}`,
      verdict: !cfgFalse && fenced ? verdict.verified("env-gated refusal") : verdict.fail(),
      pass: !cfgFalse && fenced, signals: { cfgFalse, fenced },
    });

    // positive leg: env-configured → submit proceeds via the env path (FILED).
    process.env.GOAML_ENDPOINT_URL = url;
    const cfgTrue = isGoamlConnectorConfigured();
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: true, fiaReference: "FIA-ENV-001" }) });
    captured = null;
    const envRes = await submitGoamlFormB(payload); // no override → reads env
    run.leg({
      id: "T006-FENCE-POS", scenario: "env-configured → submission proceeds (no override)",
      expectation: "isConfigured=true AND outcome FILED AND mock received a POST",
      observed: `isConfigured=${cfgTrue}; outcome=${envRes.outcome}; mockReceived=${captured !== null}`,
      verdict: cfgTrue && envRes.outcome === "FILED" && captured !== null ? verdict.verified("env-config send") : verdict.fail(),
      pass: cfgTrue && envRes.outcome === "FILED" && captured !== null, signals: { cfgTrue, outcome: envRes.outcome },
    });
    delete process.env.GOAML_ENDPOINT_URL;

    // ── PAYLOAD well-formedness (paired) ──────────────────────────────────────────
    // positive: complete payload transmitted; mock-received body has every mandatory field.
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: true, fiaReference: "FIA-WF-1" }) });
    captured = null;
    await submitGoamlFormB(payload, cfg(url));
    const b = captured?.body as Record<string, any> | undefined;
    const wellFormed = !!b
      && b.reportHeader?.rentityId && b.reportHeader?.reportCode && b.reportHeader?.entityReference
      && b.reportingPerson?.id && typeof b.reason === "string" && b.reason.length > 0
      && Array.isArray(b.reportIndicators) && b.reportIndicators.length > 0
      && b.activity?.reportParty?.reference;
    run.leg({
      id: "T006-PAYLOAD-POS", scenario: "complete payload → transmitted well-formed to endpoint",
      expectation: "mock receives a body carrying every mandatory goAML field",
      observed: `wellFormed=${!!wellFormed}; reportCode=${b?.reportHeader?.reportCode}; indicators=${b?.reportIndicators?.length}`,
      verdict: wellFormed ? verdict.verified("mandatory fields present at mock") : verdict.fail(),
      pass: !!wellFormed, signals: { wellFormed: !!wellFormed },
    });

    // negative: incomplete payload (strip the mandatory narrative `reason`) → refused PRE-send.
    const broken = JSON.parse(JSON.stringify(payload)); broken.reason = "";
    captured = null;
    let refusedPreSend = false; let missingReason = false;
    try {
      await submitGoamlFormB(broken, cfg(url));
    } catch (e) {
      refusedPreSend = e instanceof GoamlIncompletePayloadError;
      missingReason = e instanceof GoamlIncompletePayloadError && e.missingFields.includes("reason");
    }
    run.leg({
      id: "T006-PAYLOAD-NEG", scenario: "incomplete payload (missing mandatory) → refused before transmit",
      expectation: "throws GoamlIncompletePayloadError(reason) AND nothing reaches the endpoint",
      observed: `threw=${refusedPreSend}; missingIncludesReason=${missingReason}; mockReceived=${captured !== null}`,
      verdict: refusedPreSend && missingReason && captured === null ? verdict.verified("pre-send mandatory guard") : verdict.fail(),
      pass: refusedPreSend && missingReason && captured === null, signals: { refusedPreSend, missingReason, transmitted: captured !== null },
    });

    // ── ACK mapping ───────────────────────────────────────────────────────────────
    // FILED: 2xx + real reference, no ack.
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: true, fiaReference: "FIA-2026-FILED-77" }) });
    const filed = await submitGoamlFormB(payload, cfg(url));
    const filedOk = filed.outcome === "FILED" && filed.fiaReference === "FIA-2026-FILED-77" && filed.filedAt !== null && filed.acknowledgedAt === null;
    run.leg({
      id: "T006-ACK-FILED", scenario: "2xx with real fiaReference (no ack) → FILED",
      expectation: "outcome FILED; fiaReference set; filedAt set; acknowledgedAt NULL",
      observed: `outcome=${filed.outcome}; ref=${filed.fiaReference}; filedAt=${filed.filedAt !== null}; ackAt=${filed.acknowledgedAt !== null}`,
      verdict: filedOk ? verdict.verified("FILED mapping") : verdict.fail(),
      pass: filedOk, signals: { outcome: filed.outcome, ref: filed.fiaReference, filedAt: filed.filedAt !== null, ackAt: filed.acknowledgedAt !== null },
    });

    // ACKNOWLEDGED: 2xx + reference + explicit ack.
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: true, acknowledged: true, fiaReference: "FIA-2026-ACK-88", acknowledgedAt: "2026-06-16T12:00:00.000Z" }) });
    const ack = await submitGoamlFormB(payload, cfg(url));
    const ackOk = ack.outcome === "ACKNOWLEDGED" && ack.fiaReference === "FIA-2026-ACK-88" && ack.filedAt !== null && ack.acknowledgedAt !== null;
    run.leg({
      id: "T006-ACK-ACKD", scenario: "2xx with reference + acknowledged=true → ACKNOWLEDGED",
      expectation: "outcome ACKNOWLEDGED; all three filing columns set",
      observed: `outcome=${ack.outcome}; ref=${ack.fiaReference}; filedAt=${ack.filedAt !== null}; ackAt=${ack.acknowledgedAt?.toISOString?.() ?? null}`,
      verdict: ackOk ? verdict.verified("ACKNOWLEDGED mapping") : verdict.fail(),
      pass: ackOk, signals: { outcome: ack.outcome, allThree: ackOk },
    });

    // NO-FABRICATION fence: 2xx but NO reference → REJECTED, fiaReference stays NULL.
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: true }) });
    const noref = await submitGoamlFormB(payload, cfg(url));
    const norefOk = noref.outcome === "REJECTED" && noref.fiaReference === null && noref.filedAt === null && noref.acknowledgedAt === null;
    run.leg({
      id: "T006-ACK-NOFAB", scenario: "2xx WITHOUT a usable reference → NOT recorded as filed (no fabrication)",
      expectation: "outcome REJECTED; fiaReference NULL; no filed/ack columns",
      observed: `outcome=${noref.outcome}; ref=${noref.fiaReference}; filedAt=${noref.filedAt}; ackAt=${noref.acknowledgedAt}`,
      verdict: norefOk ? verdict.verified("no-fabricated-reference fence") : verdict.fail(),
      pass: norefOk, signals: { outcome: noref.outcome, ref: noref.fiaReference },
    });

    // REJECTED: non-2xx → REJECTED, no columns.
    responder = () => ({ status: 422, body: JSON.stringify({ error: "schema_invalid" }) });
    const rej = await submitGoamlFormB(payload, cfg(url));
    const rejOk = rej.outcome === "REJECTED" && rej.httpStatus === 422 && rej.fiaReference === null;
    run.leg({
      id: "T006-ACK-REJECT", scenario: "non-2xx from endpoint → REJECTED",
      expectation: "outcome REJECTED; httpStatus 422; no filing columns",
      observed: `outcome=${rej.outcome}; status=${rej.httpStatus}; ref=${rej.fiaReference}`,
      verdict: rejOk ? verdict.verified("non-2xx rejection") : verdict.fail(),
      pass: rejOk, signals: { outcome: rej.outcome, status: rej.httpStatus },
    });

    // explicit application-level rejection: 2xx but accepted=false (even WITH a reference) → REJECTED.
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: false, fiaReference: "ERR-SHOULD-NOT-FILE" }) });
    const acceptedFalse = await submitGoamlFormB(payload, cfg(url));
    const afOk = acceptedFalse.outcome === "REJECTED" && acceptedFalse.fiaReference === null;
    run.leg({
      id: "T006-ACK-FALSE", scenario: "2xx with accepted=false (even with a reference) → REJECTED (not FILED)",
      expectation: "outcome REJECTED; fiaReference NULL despite reference in body",
      observed: `outcome=${acceptedFalse.outcome}; ref=${acceptedFalse.fiaReference}`,
      verdict: afOk ? verdict.verified("accepted=false rejection") : verdict.fail(),
      pass: afOk, signals: { outcome: acceptedFalse.outcome, ref: acceptedFalse.fiaReference },
    });

    // ack flag WITHOUT a parseable timestamp → must NOT claim ACKNOWLEDGED; degrades to FILED (no fab ack time).
    responder = () => ({ status: 200, body: JSON.stringify({ acknowledged: true, fiaReference: "FIA-NO-TS" }) });
    const ackNoTs = await submitGoamlFormB(payload, cfg(url));
    const ntOk = ackNoTs.outcome === "FILED" && ackNoTs.fiaReference === "FIA-NO-TS" && ackNoTs.acknowledgedAt === null;
    run.leg({
      id: "T006-ACK-NOTS", scenario: "acknowledged=true but no acknowledgedAt → FILED (no fabricated ack time)",
      expectation: "outcome FILED; reference set; acknowledgedAt NULL",
      observed: `outcome=${ackNoTs.outcome}; ref=${ackNoTs.fiaReference}; ackAt=${ackNoTs.acknowledgedAt}`,
      verdict: ntOk ? verdict.verified("ack-without-timestamp degrades to FILED") : verdict.fail(),
      pass: ntOk, signals: { outcome: ackNoTs.outcome, ackAt: ackNoTs.acknowledgedAt },
    });

    // ack flag with an UNPARSEABLE timestamp → likewise FILED, never a synthesised ack time.
    responder = () => ({ status: 200, body: JSON.stringify({ acknowledged: true, fiaReference: "FIA-BAD-TS", acknowledgedAt: "not-a-date" }) });
    const ackBadTs = await submitGoamlFormB(payload, cfg(url));
    const btOk = ackBadTs.outcome === "FILED" && ackBadTs.acknowledgedAt === null;
    run.leg({
      id: "T006-ACK-BADTS", scenario: "acknowledged=true with unparseable acknowledgedAt → FILED (no fabricated ack time)",
      expectation: "outcome FILED; acknowledgedAt NULL",
      observed: `outcome=${ackBadTs.outcome}; ackAt=${ackBadTs.acknowledgedAt}`,
      verdict: btOk ? verdict.verified("bad-timestamp degrades to FILED") : verdict.fail(),
      pass: btOk, signals: { outcome: ackBadTs.outcome, ackAt: ackBadTs.acknowledgedAt },
    });

    // ── AUTH header (paired) ──────────────────────────────────────────────────────
    // positive: bearer configured → endpoint sees the Authorization header.
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: true, fiaReference: "FIA-AUTH-1" }) });
    captured = null;
    await submitGoamlFormB(payload, cfg(url, { authType: "bearer", authToken: "tok-proof-xyz" }));
    const authHdr = captured?.headers["authorization"];
    const authPos = authHdr === "Bearer tok-proof-xyz";
    run.leg({
      id: "T006-AUTH-POS", scenario: "bearer auth configured → Authorization header sent",
      expectation: "endpoint receives Authorization: Bearer <token>",
      observed: `authPresent=${authHdr !== undefined}; matches=${authPos}`,
      verdict: authPos ? verdict.verified("bearer header round-trip") : verdict.fail(),
      pass: authPos, signals: { authPos },
    });

    // negative: no auth → endpoint sees NO Authorization header (paired control).
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: true, fiaReference: "FIA-AUTH-2" }) });
    captured = null;
    await submitGoamlFormB(payload, cfg(url, { authType: "none", authToken: null }));
    const authNeg = captured?.headers["authorization"] === undefined;
    run.leg({
      id: "T006-AUTH-NEG", scenario: "no auth configured → NO Authorization header",
      expectation: "endpoint receives no Authorization header",
      observed: `authAbsent=${authNeg}`,
      verdict: authNeg ? verdict.verified("auth-absent control") : verdict.fail(),
      pass: authNeg, signals: { authNeg },
    });

    // ── SECRET HYGIENE (Rule 1): endpoint echoes our Authorization back → token redacted ─
    responder = (req) => ({
      status: 200,
      // a misbehaving endpoint reflecting the request's Authorization header into its body
      body: JSON.stringify({ accepted: true, fiaReference: "FIA-ECHO-1", echoedAuth: req.headers["authorization"] }),
    });
    const echoTok = "tok-secret-should-be-redacted";
    const echoed = await submitGoamlFormB(payload, cfg(url, { authType: "bearer", authToken: echoTok }));
    const redactOk =
      echoed.outcome === "FILED" &&
      echoed.rawResponseBody !== null &&
      !echoed.rawResponseBody.includes(echoTok) &&
      echoed.rawResponseBody.includes("[REDACTED]");
    run.leg({
      id: "T006-REDACT", scenario: "endpoint echoes Authorization back → token redacted from retained body",
      expectation: "rawResponseBody contains NO token AND a [REDACTED] marker",
      observed: `outcome=${echoed.outcome}; containsToken=${echoed.rawResponseBody?.includes(echoTok)}; hasRedacted=${echoed.rawResponseBody?.includes("[REDACTED]")}`,
      verdict: redactOk ? verdict.verified("auth-token redaction") : verdict.fail(),
      pass: redactOk, signals: { containsToken: echoed.rawResponseBody?.includes(echoTok), redacted: echoed.rawResponseBody?.includes("[REDACTED]") },
    });

    // ── TRANSPORT error: timeout → TRANSPORT_ERROR, no fabricated ack ─────────────
    responder = () => ({ status: 200, body: JSON.stringify({ accepted: true, fiaReference: "SHOULD-NOT-BE-READ" }), delayMs: 300 });
    const to = await submitGoamlFormB(payload, cfg(url, { timeoutMs: 50 }));
    const toOk = to.outcome === "TRANSPORT_ERROR" && to.fiaReference === null && to.httpStatus === null;
    run.leg({
      id: "T006-TRANSPORT", scenario: "endpoint slower than timeout → TRANSPORT_ERROR (no fabricated filing)",
      expectation: "outcome TRANSPORT_ERROR; no reference; no http status",
      observed: `outcome=${to.outcome}; ref=${to.fiaReference}; status=${to.httpStatus}`,
      verdict: toOk ? verdict.verified("timeout → transport error") : verdict.fail(),
      pass: toOk, signals: { outcome: to.outcome },
    });
  } finally {
    delete process.env.GOAML_ENDPOINT_URL;
    delete process.env.GOAML_AUTH_TYPE;
    delete process.env.GOAML_AUTH_TOKEN;
    await new Promise<void>((r) => server.close(() => r()));
  }

  run.report();
  await run.writeJson();
  process.exit(run.acceptance().allPass ? 0 : 1);
}

main().catch((e) => {
  console.error("FATAL", e);
  process.exit(1);
});
