import { createServer } from "node:http";
import { describe, expect, it } from "vitest";
import {
  unsafeOpaqueId,
  type ConnectionId,
  type CorrelationId,
  type CredentialReferenceId,
  type FactoryRunId,
  type OperationId,
  type PlanRevisionId,
  type PrincipalId,
  type ProviderOperationContext,
} from "@awp/contracts";
import {
  FABRO_PROVIDER_ID,
  FabroFactoryProvider,
  FabroHttpClient,
  type FabroNativeClient,
  type FabroNativeRun,
} from "../../../packages/providers/factory-fabro/src/index.js";

function context(): ProviderOperationContext {
  return {
    operationId: unsafeOpaqueId<OperationId>("op-factory-1"),
    idempotencyKey: "idem-factory-1",
    correlationId: unsafeOpaqueId<CorrelationId>("corr-factory-1"),
    connectionId: unsafeOpaqueId<ConnectionId>("connection-fabro"),
    credentialReferenceId: unsafeOpaqueId<CredentialReferenceId>("credential-fabro"),
    authority: {
      principal: {
        id: unsafeOpaqueId<PrincipalId>("principal-system"),
        kind: "system",
        capabilities: [],
      },
      capabilities: [],
    },
  };
}

class FakeFabroClient implements FabroNativeClient {
  run?: FabroNativeRun;
  startCalls = 0;
  cancelCalls = 0;
  dropStartResponseOnce = false;

  async findRunByIdempotencyKey(): Promise<FabroNativeRun | undefined> {
    return this.run;
  }

  async findRunByFactoryRunId(factoryRunId: string): Promise<FabroNativeRun | undefined> {
    return this.run?.factoryRunId === factoryRunId ? this.run : undefined;
  }

  async startRun(input: {
    readonly factoryRunId: string;
    readonly planRevisionId: string;
    readonly idempotencyKey: string;
  }): Promise<FabroNativeRun> {
    this.startCalls += 1;
    this.run = {
      id: "fabro-native-1",
      factoryRunId: input.factoryRunId,
      planRevisionId: input.planRevisionId,
      status: "running",
      currentStage: "implement",
      checkpointId: "checkpoint-1",
    };
    if (this.dropStartResponseOnce) {
      this.dropStartResponseOnce = false;
      throw { status: 503 };
    }
    return this.run;
  }

  async cancelRun(): Promise<FabroNativeRun> {
    this.cancelCalls += 1;
    if (this.run === undefined) throw { status: 404 };
    this.run = { ...this.run, status: "cancelled" };
    return this.run;
  }

  async getRun(): Promise<FabroNativeRun | undefined> {
    return this.run;
  }
}

const factoryRunId = unsafeOpaqueId<FactoryRunId>("factory-run-1");
const planRevisionId = unsafeOpaqueId<PlanRevisionId>("plan-revision-1");

describe("FabroFactoryProvider", () => {
  it("maps FactoryRun identity to a Fabro run and exposes checkpoint observation", async () => {
    const client = new FakeFabroClient();
    const result = await new FabroFactoryProvider(client).start(
      context(),
      factoryRunId,
      planRevisionId,
    );

    expect(client.startCalls).toBe(1);
    expect(result.value).toMatchObject({
      state: "running",
      details: {
        factoryRunId: "factory-run-1",
        planRevisionId: "plan-revision-1",
        currentStage: "implement",
        checkpointId: "checkpoint-1",
      },
    });
  });

  it("replays start idempotently without a second provider mutation", async () => {
    const client = new FakeFabroClient();
    const provider = new FabroFactoryProvider(client);
    await provider.start(context(), factoryRunId, planRevisionId);
    await provider.start(context(), factoryRunId, planRevisionId);
    expect(client.startCalls).toBe(1);
  });

  it("B-FIRE-MUT-001 reconciles ambiguous Fabro start success before repeating mutation", async () => {
    const client = new FakeFabroClient();
    client.dropStartResponseOnce = true;
    const provider = new FabroFactoryProvider(client);

    await expect(provider.start(context(), factoryRunId, planRevisionId)).rejects.toMatchObject({
      providerError: { category: "unavailable", retryable: true },
    });
    expect(client.startCalls).toBe(1);
    expect(client.run?.status).toBe("running");

    const retry = await provider.start(context(), factoryRunId, planRevisionId);
    expect(retry.value.state).toBe("running");
    expect(client.startCalls).toBe(1);
  });

  it("rejects an idempotency collision with different AWP run identity", async () => {
    const client = new FakeFabroClient();
    client.run = {
      id: "fabro-native-foreign",
      factoryRunId: "factory-run-other",
      planRevisionId: "plan-revision-1",
      status: "running",
    };

    await expect(
      new FabroFactoryProvider(client).start(context(), factoryRunId, planRevisionId),
    ).rejects.toMatchObject({ providerError: { category: "conflict-stale" } });
  });

  it("RT-034-B keeps provider workflow completion semantic-neutral for Task completion", async () => {
    const client = new FakeFabroClient();
    client.run = {
      id: "fabro-native-completed",
      factoryRunId: "factory-run-1",
      planRevisionId: "plan-revision-1",
      status: "completed",
    };

    const reconciled = await new FabroFactoryProvider(client).reconcile({
      context: context(),
      reference: {
        providerId: FABRO_PROVIDER_ID,
        resourceType: "factory-run",
        nativeId: "fabro-native-completed",
        observedAt: new Date().toISOString(),
      },
    });

    expect(reconciled.value.state).toBe("completed");
    expect(reconciled.value.details).not.toHaveProperty("taskStatus");
    expect(reconciled.value.details).not.toHaveProperty("taskCompleted");
    expect(reconciled.value.details).not.toHaveProperty("semanticOutcome");
  });

  it("cancels an active run once and treats terminal cancellation as replay-safe", async () => {
    const client = new FakeFabroClient();
    const provider = new FabroFactoryProvider(client);
    await provider.start(context(), factoryRunId, planRevisionId);
    const cancelled = await provider.cancel(context(), factoryRunId);
    const replay = await provider.cancel(context(), factoryRunId);

    expect(cancelled.value.state).toBe("cancelled");
    expect(replay.value.state).toBe("cancelled");
    expect(client.cancelCalls).toBe(1);
  });

  it("normalizes provider outages as retryable", async () => {
    const client: FabroNativeClient = {
      async findRunByIdempotencyKey() {
        throw { status: 503 };
      },
      async findRunByFactoryRunId() {
        return undefined;
      },
      async startRun() {
        throw new Error("unreachable");
      },
      async cancelRun() {
        throw new Error("unreachable");
      },
      async getRun() {
        return undefined;
      },
    };

    await expect(
      new FabroFactoryProvider(client).start(context(), factoryRunId, planRevisionId),
    ).rejects.toMatchObject({
      providerError: { category: "unavailable", retryable: true },
    });
  });
});

describe("FabroHttpClient", () => {
  it("uses the pinned Fabro /api/v1 run contract with durable AWP metadata", async () => {
    const seenAuthorization: string[] = [];
    let submittedManifest: Record<string, unknown> | undefined;
    let run = {
      id: "01KAWPFACTORY00000000000001",
      labels: {
        awp_factory_run_id: "factory-run-http",
        awp_plan_revision_id: "plan-revision-http",
        awp_idempotency_key: "idem-http",
      },
      lifecycle: { status: { kind: "submitted" }, pending_control: null },
      links: { web: "https://fabro.test/runs/01KAWPFACTORY00000000000001" },
    };
    const server = createServer(async (request, response) => {
      seenAuthorization.push(request.headers.authorization ?? "");
      const url = new URL(request.url ?? "/", "http://127.0.0.1");
      if (request.method === "GET" && url.pathname === "/api/v1/runs") {
        response.writeHead(200, { "content-type": "application/json" });
        response.end(JSON.stringify({ data: [], meta: { has_more: false, total: 0 } }));
        return;
      }
      if (request.method === "POST" && url.pathname === "/api/v1/runs") {
        const chunks: Buffer[] = [];
        for await (const chunk of request) chunks.push(Buffer.from(chunk));
        submittedManifest = JSON.parse(Buffer.concat(chunks).toString("utf8")) as Record<
          string,
          unknown
        >;
        response.writeHead(201, { "content-type": "application/json" });
        response.end(JSON.stringify(run));
        return;
      }
      if (
        request.method === "POST" &&
        url.pathname === `/api/v1/runs/${encodeURIComponent(run.id)}/start`
      ) {
        run = {
          ...run,
          lifecycle: { status: { kind: "running" }, pending_control: null },
        };
        response.writeHead(200, { "content-type": "application/json" });
        response.end(JSON.stringify(run));
        return;
      }
      if (request.method === "GET" && url.pathname.endsWith("/timeline")) {
        response.writeHead(200, { "content-type": "application/json" });
        response.end(
          JSON.stringify([
            {
              ordinal: 1,
              node_name: "coordinate",
              visit: 1,
              checkpoint_seq: 7,
              run_commit_sha: null,
            },
          ]),
        );
        return;
      }
      response.writeHead(404).end();
    });
    await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
    try {
      const address = server.address();
      if (typeof address !== "object" || address === null) throw new Error("server did not bind");
      const client = new FabroHttpClient({
        baseUrl: `http://127.0.0.1:${address.port}`,
        bearerToken: "fabro-dogfood-control-token",
      });
      const result = await client.startRun({
        factoryRunId: "factory-run-http",
        planRevisionId: "plan-revision-http",
        idempotencyKey: "idem-http",
      });

      expect(result).toEqual({
        id: run.id,
        factoryRunId: "factory-run-http",
        planRevisionId: "plan-revision-http",
        status: "running",
        currentStage: "coordinate",
        checkpointId: "7",
        url: "https://fabro.test/runs/01KAWPFACTORY00000000000001",
      });
      expect(new Set(seenAuthorization)).toEqual(new Set(["Bearer fabro-dogfood-control-token"]));
      const workflows = submittedManifest?.workflows as Record<
        string,
        { source: string; config: { source: string } }
      >;
      expect(submittedManifest?.target).toEqual({ path: "awp-factory.fabro" });
      expect(workflows["awp-factory.fabro"]?.source).toContain("start -> complete");
      expect(workflows["awp-factory.fabro"]?.config.source).toContain(
        'awp_factory_run_id = "factory-run-http"',
      );
      expect(workflows["awp-factory.fabro"]?.config.source).toContain(
        'awp_plan_revision_id = "plan-revision-http"',
      );
      expect(workflows["awp-factory.fabro"]?.config.source).toContain(
        'awp_idempotency_key = "idem-http"',
      );
    } finally {
      await new Promise<void>((resolve, reject) =>
        server.close((error) => (error ? reject(error) : resolve())),
      );
    }
  });

  it("reconciles runs by durable metadata and maps Fabro cancellation state", async () => {
    const runId = "01KAWPFACTORY00000000000002";
    let cancelCalls = 0;
    const run = (pendingControl: string | null = null) => ({
      id: runId,
      labels: {
        awp_factory_run_id: "factory-run-reconcile",
        awp_plan_revision_id: "plan-revision-reconcile",
        awp_idempotency_key: "idem-reconcile",
      },
      lifecycle: { status: { kind: "running" }, pending_control: pendingControl },
      links: { web: null },
    });
    const server = createServer((request, response) => {
      const url = new URL(request.url ?? "/", "http://127.0.0.1");
      response.setHeader("content-type", "application/json");
      if (request.method === "GET" && url.pathname === "/api/v1/runs") {
        response.end(JSON.stringify({ data: [run()], meta: { has_more: false, total: 1 } }));
        return;
      }
      if (request.method === "GET" && url.pathname.endsWith("/timeline")) {
        response.end(JSON.stringify([]));
        return;
      }
      if (request.method === "POST" && url.pathname.endsWith("/cancel")) {
        cancelCalls += 1;
        response.writeHead(202);
        response.end(JSON.stringify(run("cancel")));
        return;
      }
      response.writeHead(404).end(JSON.stringify({ detail: "not found" }));
    });
    await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
    try {
      const address = server.address();
      if (typeof address !== "object" || address === null) throw new Error("server did not bind");
      const client = new FabroHttpClient({ baseUrl: `http://127.0.0.1:${address.port}` });
      const reconciled = await client.findRunByIdempotencyKey("idem-reconcile");
      expect(reconciled).toMatchObject({
        id: runId,
        factoryRunId: "factory-run-reconcile",
        planRevisionId: "plan-revision-reconcile",
        status: "running",
      });
      expect(await client.cancelRun(runId)).toMatchObject({ status: "cancelling" });
      expect(cancelCalls).toBe(1);
    } finally {
      await new Promise<void>((resolve, reject) =>
        server.close((error) => (error ? reject(error) : resolve())),
      );
    }
  });
});
