import { describe, expect, test } from "bun:test";
import { observePacDeliveryBudget, observePacHistoryDeliveryBudget, observePacStatusDeliveryBudget, pacDeliveryTimingWindow } from "./platform-infra-pac-delivery-timing";

const policy = {
  endToEndBudgetSeconds: 120,
  configPath: "config/platform-infra/pipelines-as-code.yaml#deliveryTiming.endToEndBudgetSeconds",
};

function observe(endToEndSeconds: number | null, complete: boolean) {
  return observePacDeliveryBudget({
    policy,
    targetId: "NC01",
    consumerId: "selfmedia-nc01",
    pipelineRunId: "selfmedia-nc01-fixture",
    endToEndSeconds,
    complete,
  });
}

describe("PaC manual delivery budget warning", () => {
  test("manual delivery timing starts at PaC webhook admission instead of PR merge", () => {
    expect(pacDeliveryTimingWindow({
      triggeredAt: "2026-07-21T11:35:42Z",
      startTime: "2026-07-21T11:35:52Z",
      completionTime: "2026-07-21T11:37:42Z",
      durationSeconds: 110,
    })).toEqual({
      deliveryStart: "2026-07-21T11:35:42Z",
      deliveryStartSource: "pac-webhook-admission",
      pipelineStart: "2026-07-21T11:35:52Z",
      pipelineCompletion: "2026-07-21T11:37:42Z",
      triggerWaitSeconds: 10,
      pipelineDurationSeconds: 110,
      endToEndSeconds: 120,
    });
  });

  test("missing webhook admission stays unknown instead of falling back to PR merge", () => {
    expect(pacDeliveryTimingWindow({
      startTime: "2026-07-21T11:35:52Z",
      completionTime: "2026-07-21T11:37:42Z",
    })).toMatchObject({ deliveryStart: null, deliveryStartSource: null, triggerWaitSeconds: null, endToEndSeconds: null });
  });

  test("120 seconds stays within budget without a warning", () => {
    const result = observe(120, true);
    expect(result).toMatchObject({ state: "complete", overBudget: false, warning: null, blocking: false, mutation: false });
  });

  test("more than 120 seconds emits a non-blocking env reuse warning", () => {
    const result = observe(121, true);
    expect(result.overBudget).toBe(true);
    expect(result.warning).toMatchObject({
      code: "pac-manual-delivery-over-budget",
      severity: "warning",
      blocking: false,
      mutation: false,
      observedSeconds: 121,
      budgetSeconds: 120,
      configPath: policy.configPath,
    });
    const warningText = JSON.stringify(result.warning);
    expect(warningText).toContain("env reuse");
    expect(warningText).toContain("BuildKit/cache");
    expect(warningText).not.toMatch(/trigger-current|refresh|sync|PipelineRun create/iu);
  });

  test("missing timing evidence remains partial and never warns", () => {
    const result = observe(null, false);
    expect(result).toMatchObject({ state: "partial", overBudget: null, warning: null, blocking: false, mutation: false });
  });

  test("status and history summaries stay partial without exact end-to-end evidence", () => {
    const status = observePacStatusDeliveryBudget({
      policy,
      targetId: "NC01",
      consumerId: "selfmedia-nc01",
      summary: { ready: true, latestPipelineRun: { name: "fixture", durationSeconds: 193 } },
    });
    const history = observePacHistoryDeliveryBudget({
      policy,
      targetId: "NC01",
      consumerIds: ["selfmedia-nc01"],
      defaultConsumerId: "selfmedia-nc01",
      rows: [{ id: "fixture", consumer: "selfmedia-nc01", durationSeconds: 193 }],
    });
    expect(status).toMatchObject({ state: "partial", overBudget: null, warning: null, pipelineDurationSeconds: 193, mutation: false });
    expect(history).toMatchObject({ state: "partial", blocking: false, mutation: false });
    expect((history.observations as Array<Record<string, unknown>>)[0]).toMatchObject({ state: "partial", warning: null, pipelineDurationSeconds: 193 });
  });
});
