import assert from "node:assert/strict";
import { chmod, mkdtemp, mkdir, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";

const TEST_AGENT_USERNAME = "server-test-agent";
const TEST_AGENT_PASSWORD = "server-test-agent-password";
const testAuthByPort = new Map();

export function durableBlockedRuntime({ blocker, blockedLayer, queryResult, gates }) {
  return {
    adapter: "postgres",
    durable: false,
    durableRequested: true,
    durableCapable: false,
    ready: false,
    status: "blocked",
    blocker,
    reason: `test durable runtime blocker ${blocker}`,
    liveRuntimeEvidence: false,
    fixtureEvidence: false,
    connection: {
      queryAttempted: true,
      queryResult,
      endpointRedacted: true,
      valueRedacted: true,
      errorCode: "TEST_BLOCKER"
    },
    schema: {
      checked: gates.schema !== "not_checked",
      ready: gates.schema === "ready",
      missingTables: gates.schema === "blocked" ? ["gateway_sessions"] : [],
      missingColumns: gates.schema === "blocked" ? ["gateway_sessions.gateway_session_json"] : []
    },
    migration: {
      checked: gates.migration !== "not_checked",
      ready: gates.migration === "ready",
      missing: gates.migration !== "ready"
    },
    gates: Object.fromEntries(Object.entries(gates).map(([name, status]) => [
      name,
      {
        checked: status !== "not_checked",
        ready: status === "ready",
        status,
        blocker: status === "blocked" ? blocker : null
      }
    ])),
    durabilityContract: {
      ready: false,
      status: "blocked",
      requiredEvidence: "runtime_adapter_schema_migration_read_query",
      dbLiveEvidenceIsDurabilityEvidence: false,
      liveRuntimeEvidence: false,
      adapterQueryRequired: true,
      blockedLayer,
      blocker,
      secretMaterialRead: false
    },
    safety: {
      secretMaterialRead: false,
      valuesRedacted: true,
      endpointRedacted: true
    },
    counts: {}
  };
}

export function durableReadyRuntime() {
  return {
    adapter: "postgres",
    durable: true,
    durableRequested: true,
    durableCapable: true,
    ready: true,
    status: "ready",
    blocker: null,
    reason: "test durable runtime ready",
    liveRuntimeEvidence: true,
    fixtureEvidence: false,
    connection: {
      queryAttempted: true,
      queryResult: "durable_readiness_ready",
      endpointRedacted: true,
      valueRedacted: true
    },
    schema: {
      checked: true,
      ready: true,
      missingTables: [],
      missingColumns: []
    },
    migration: {
      checked: true,
      ready: true,
      missing: false
    },
    gates: Object.fromEntries(["ssl", "auth", "schema", "migration", "durability"].map((name) => [
      name,
      {
        checked: true,
        ready: true,
        status: "ready",
        blocker: null
      }
    ])),
    durabilityContract: {
      ready: true,
      status: "ready",
      requiredEvidence: "runtime_adapter_schema_migration_read_query",
      dbLiveEvidenceIsDurabilityEvidence: false,
      liveRuntimeEvidence: true,
      adapterQueryRequired: true,
      blockedLayer: null,
      blocker: null,
      secretMaterialRead: false
    },
    safety: {
      secretMaterialRead: false,
      valuesRedacted: true,
      endpointRedacted: true
    },
    counts: {}
  };
}

export function layerStatus(gateStatus) {
  if (gateStatus === "ready") return "pass";
  if (gateStatus === "blocked") return "blocked";
  return "not_proven";
}

export function layerForBlockedCase(blockedLayer) {
  if (blockedLayer === "durability_query") return "durability";
  return blockedLayer;
}

export async function m3ReadinessRequestJson(url, request = {}) {
  const parsed = new URL(url);
  const body = request.body ?? {};
  if (url.startsWith("http://gateway-1") && parsed.pathname === "/status") {
    return {
      ok: true,
      status: 200,
      body: m3GatewayStatus({
        gatewayId: "gwsimu_1",
        gatewaySessionId: "gws_gwsimu_1",
        boxId: "boxsimu_1",
        resourceId: "res_boxsimu_1"
      })
    };
  }
  if (url.startsWith("http://gateway-1") && parsed.pathname === "/invoke") {
    return {
      ok: true,
      status: 200,
      body: {
        accepted: true,
        operationId: "op_m3_status_do1",
        traceId: "trc_m3_status_do1",
        boxId: "boxsimu_1",
        resourceId: "res_boxsimu_1",
        port: "DO1",
        value: false,
        state: {
          port: "DO1",
          direction: "output",
          value: false,
          source: "gateway-simu",
          updatedAt: "2026-05-23T00:00:00.000Z"
        },
        auditId: "aud_m3_status_do1",
        evidenceId: "evd_m3_status_do1"
      }
    };
  }
  if (url.startsWith("http://gateway-2") && parsed.pathname === "/status") {
    return {
      ok: true,
      status: 200,
      body: m3GatewayStatus({
        gatewayId: "gwsimu_2",
        gatewaySessionId: "gws_gwsimu_2",
        boxId: "boxsimu_2",
        resourceId: "res_boxsimu_2"
      })
    };
  }
  if (url.startsWith("http://gateway-2") && parsed.pathname === "/invoke") {
    const value = request.body?.operationId?.includes("do_write") || request.body?.operationId?.includes("do-write")
      ? true
      : false;
    return {
      ok: true,
      status: 200,
      body: {
        accepted: true,
        operationId: "op_m3_status_di1",
        traceId: "trc_m3_status_di1",
        boxId: "boxsimu_2",
        resourceId: "res_boxsimu_2",
        port: "DI1",
        value,
        state: {
          port: "DI1",
          direction: "input",
          value,
          source: "patch-panel",
          sourceResourceId: "res_boxsimu_1",
          sourcePort: "DO1",
          propagatedBy: "hwlab-patch-panel",
          updatedAt: "2026-05-23T00:00:00.000Z"
        },
        auditId: "aud_m3_status_di1",
        evidenceId: "evd_m3_status_di1"
      }
    };
  }
  if (url.startsWith("http://patch-panel") && parsed.pathname === "/status") {
    return {
      ok: true,
      status: 200,
      body: {
        serviceId: "hwlab-patch-panel",
        state: "active",
        activeConnections: [
          {
            fromResourceId: "res_boxsimu_1",
            fromPort: "DO1",
            toResourceId: "res_boxsimu_2",
            toPort: "DI1"
          }
        ]
      }
    };
  }
  if (url.startsWith("http://patch-panel") && parsed.pathname === "/wiring") {
    return {
      ok: true,
      status: 200,
      body: {
        wiringConfigId: "wir_m3_do1_di1",
        status: "active",
        connections: [
          {
            from: {
              resourceId: "res_boxsimu_1",
              port: "DO1"
            },
            to: {
              resourceId: "res_boxsimu_2",
              port: "DI1"
            },
            mode: "exclusive"
          }
        ]
      }
    };
  }
  if (url.startsWith("http://patch-panel") && parsed.pathname === "/sync/tick") {
    return {
      ok: true,
      status: 200,
      body: {
        accepted: true,
        propagatedBy: "hwlab-patch-panel",
        routeCount: 1,
        deliveryCount: 1,
        routes: [
          {
            accepted: true,
            deliveryCount: 1,
            deliveries: [
              {
                resourceId: "res_boxsimu_2",
                port: "DI1",
                value: body.signals?.[0]?.value ?? false,
                sourceResourceId: "res_boxsimu_1",
                sourcePort: "DO1",
                deliveryStatus: "applied"
              }
            ]
          }
        ]
      }
    };
  }
  throw new Error(`unexpected M3 readiness request ${url}`);
}

export function m3GatewayStatus({ gatewayId, gatewaySessionId, boxId, resourceId }) {
  return {
    serviceId: "hwlab-gateway-simu",
    gatewayId,
    gatewaySessionId,
    session: {
      gatewayId,
      gatewaySessionId
    },
    registry: {
      gatewayId,
      gatewaySessionId,
      boxes: [
        {
          boxId,
          resourceId,
          state: "registered"
        }
      ]
    }
  };
}

export function codexStdioReadyFixture({ workspace, codexHome }) {
  return {
    kind: "codex-app-server-stdio-runner",
    provider: "codex-stdio",
    backend: "hwlab-cloud-api/codex-app-server-stdio",
    status: "feasible",
    ready: true,
    startupReady: true,
    canStartLongLivedCodexStdio: true,
    command: path.join(process.cwd(), "node_modules", ".bin", "codex"),
    binaryOnPath: true,
    binary: {
      present: true,
      executable: true,
      nativeDependencyPresent: true,
      versionDetected: true
    },
    workspace,
    workspaceState: { exists: true, readable: true, writable: true, writeRequired: true },
    codexHome,
    codexHomeState: { exists: true, readable: true, writable: true, writeRequired: true },
    sandbox: "workspace-write",
    enabled: true,
    supervisor: { configured: true, mode: "repo-owned-node-supervisor" },
    tokenBoundary: { present: true, secretMaterialRead: false, valuesRedacted: true },
    egress: { configured: true, directPublicOpenAi: false, valueRedacted: true },
    protocol: {
      status: "wired",
      wired: true,
      requiredTools: ["codex", "codex-reply"],
      toolsObserved: ["codex", "codex-reply"],
      missingTools: [],
      command: "codex app-server --listen stdio://"
    },
    commandProbe: {
      status: "ready",
      ready: true,
      probe: "workspace.pwd",
      traceId: "trc_codex_stdio_command_probe",
      toolCalls: [{
        name: "pwd",
        status: "completed",
        cwd: workspace,
        command: "pwd",
        exitCode: 0,
        stdoutSummary: "workspace path matched",
        outputTruncated: false
      }],
      secretMaterialRead: false,
      valuesRedacted: true
    },
    stdioProtocol: {
      status: "wired",
      wired: true,
      requiredTools: ["codex", "codex-reply"],
      toolsObserved: ["codex", "codex-reply"],
      missingTools: [],
      command: "codex app-server --listen stdio://"
    },
    sessionLifecycle: {
      status: "present",
      present: true,
      create: true,
      reuse: true,
      cancel: true,
      reap: true,
      traceCapture: true,
      idleTimeoutMs: 1800000
    },
    lifecycleSupervisor: {
      status: "present",
      present: true,
      create: true,
      reuse: true,
      cancel: true,
      reap: true,
      traceCapture: true,
      idleTimeoutMs: 1800000
    },
    runtimeContract: {
      status: "ready",
      ready: true,
      binary: {
        status: "present",
        present: true,
        executable: true,
        nativeDependencyPresent: true,
        versionDetected: true
      },
      stdioProtocol: {
        status: "wired",
        wired: true,
        requiredTools: ["codex", "codex-reply"],
        toolsObserved: ["codex", "codex-reply"],
        missingTools: []
      },
      commandProbe: {
        status: "ready",
        ready: true,
        probe: "workspace.pwd",
        traceId: "trc_codex_stdio_command_probe",
        toolCalls: [{
          name: "pwd",
          status: "completed",
          cwd: workspace,
          command: "pwd",
          exitCode: 0,
          stdoutSummary: "workspace path matched",
          outputTruncated: false
        }],
        secretMaterialRead: false,
        valuesRedacted: true
      },
      lifecycleSupervisor: {
        status: "present",
        present: true,
        create: true,
        reuse: true,
        cancel: true,
        reap: true,
        traceCapture: true,
        idleTimeoutMs: 1800000
      },
      workspaceMount: {
        path: workspace,
        status: "ready",
        mounted: true,
        readable: true,
        writable: true,
        sandbox: "workspace-write"
      },
      codexHome: {
        path: codexHome,
        status: "ready",
        exists: true,
        readable: true,
        writable: true
      },
      cancelReapTraceReadiness: {
        status: "ready",
        cancel: true,
        reap: true,
        traceCapture: true,
        idleTimeout: true
      }
    },
    blockers: [],
    blockerCodes: [],
    safety: { secretMaterialRead: false, valuesRedacted: true }
  };
}

export function codexStdioChatFixture({ workspace, codexHome, params }) {
  const traceId = params.traceId;
  const session = {
    sessionId: "ses_server_stdio_pwd",
    conversationId: params.conversationId,
    status: "idle",
    workspace,
    sandbox: "workspace-write",
    runnerKind: "codex-app-server-stdio-runner",
    sessionMode: "codex-app-server-stdio-long-lived",
    capabilityLevel: "long-lived-codex-stdio-session",
    implementationType: "repo-owned-codex-app-server-stdio-session",
    createdAt: "2026-05-23T00:00:00.000Z",
    updatedAt: "2026-05-23T00:00:00.000Z",
    idleTimeoutMs: 1800000,
    expiresAt: "2026-05-23T00:30:00.000Z",
    lastTraceId: traceId,
    turn: 1,
    threadId: "thread_server_stdio_pwd",
    reused: false,
    durable: true,
    longLivedSession: true,
    codexStdio: true,
    writeCapable: true,
    secretMaterialStored: false,
    valuesRedacted: true
  };
  const feasibility = codexStdioReadyFixture({ workspace, codexHome });
  return {
    provider: "codex-stdio",
    model: "gpt-test",
    backend: "hwlab-cloud-api/codex-app-server-stdio",
    content: `stdio pwd\n${workspace}`,
    workspace,
    sandbox: "workspace-write",
    session,
    sessionMode: "codex-app-server-stdio-long-lived",
    sessionReuse: {
      conversationId: params.conversationId,
      sessionId: session.sessionId,
      threadId: session.threadId,
      mapped: true,
      reused: false,
      turn: 1,
      previousTurns: 0,
      workspace,
      status: "idle",
      idleTimeoutMs: 1800000
    },
    implementationType: "repo-owned-codex-app-server-stdio-session",
    runnerLimitations: ["secret-values-redacted"],
    codexStdioFeasibility: feasibility,
    longLivedSessionGate: {
      status: "pass",
      pass: true,
      provider: "codex-stdio",
      runnerKind: "codex-app-server-stdio-runner",
      sessionMode: "codex-app-server-stdio-long-lived",
      implementationType: "repo-owned-codex-app-server-stdio-session",
      blockers: []
    },
    toolCalls: [{
      id: "tool_server_stdio_pwd",
      type: "workspace-read",
      name: "pwd",
      status: "completed",
      cwd: workspace,
      command: "pwd",
      exitCode: 0,
      stdout: workspace,
      stderrSummary: "",
      outputTruncated: false,
      traceId
    }],
    skills: {
      status: "not_requested",
      items: [],
      count: 0,
      blockers: []
    },
    runner: {
      kind: "codex-app-server-stdio-runner",
      provider: "codex-stdio",
      backend: "hwlab-cloud-api/codex-app-server-stdio",
      workspace,
      sandbox: "workspace-write",
      session: "codex-app-server-stdio-long-lived",
      sessionMode: "codex-app-server-stdio-long-lived",
      sessionId: session.sessionId,
      implementationType: "repo-owned-codex-app-server-stdio-session",
      codexStdio: true,
      longLivedSession: true,
      durableSession: true,
      writeCapable: true,
      readOnly: false,
      capabilityLevel: "long-lived-codex-stdio-session"
    },
    runnerTrace: {
      traceId,
      runnerKind: "codex-app-server-stdio-runner",
      workspace,
      sandbox: "workspace-write",
      sessionMode: "codex-app-server-stdio-long-lived",
      sessionId: session.sessionId,
      sessionStatus: "idle",
      turn: 1,
      implementationType: "repo-owned-codex-app-server-stdio-session",
      events: ["stdio:acquire", "session:created", "stdio:ready", "tool:pwd:completed"],
      outputTruncated: false,
      valuesPrinted: false
    },
    capabilityLevel: "long-lived-codex-stdio-session",
    providerTrace: {
      transport: "stdio",
      protocol: "codex-app-server-jsonrpc-stdio",
      command: "codex app-server --listen stdio://",
      toolName: "codex",
      threadId: "thread_server_stdio_pwd",
      valuesPrinted: false
    }
  };
}

export function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export async function postAgent(port, body) {
  const nextBody = { ...body };
  let auth = testAuthByPort.get(port) ?? null;
  const manual = nextBody.sessionId ? null : await createManualAgentSession(port, {
    conversationId: nextBody.conversationId,
    projectId: nextBody.projectId,
    providerProfile: nextBody.providerProfile
  });
  auth = auth ?? manual;
  if (manual) {
    nextBody.conversationId = manual.conversationId;
    nextBody.sessionId = manual.sessionId;
  }
  const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      ...(auth?.cookie ? { cookie: auth.cookie } : {}),
      ...(nextBody.traceId ? { "x-trace-id": nextBody.traceId } : {})
    },
    body: JSON.stringify(nextBody)
  });
  assert.equal(response.status, 200);
  return response.json();
}

export async function createManualAgentSession(port, input = {}) {
  const auth = await ensureTestAgentAuth(port);
  const response = await fetch(`http://127.0.0.1:${port}/v1/agent/sessions`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      cookie: auth.cookie
    },
    body: JSON.stringify({
      conversationId: input.conversationId,
      sessionId: input.sessionId,
      projectId: input.projectId,
      providerProfile: input.providerProfile,
      updatedByClient: "server-agent-chat-test"
    })
  });
  const body = await response.json();
  assert.equal(response.status, 201);
  assert.equal(body.ok, true);
  assert.equal(body.session.usable, true);
  return {
    cookie: auth.cookie,
    headers: {
      "content-type": "application/json",
      cookie: auth.cookie
    },
    session: body.session,
    sessionId: body.session.sessionId,
    conversationId: body.session.conversationId
  };
}

export async function ensureTestAgentAuth(port) {
  const cached = testAuthByPort.get(port);
  if (cached?.cookie) return cached;
  const setup = await fetch(`http://127.0.0.1:${port}/v1/setup/first-admin`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      username: TEST_AGENT_USERNAME,
      displayName: "Server Test Agent",
      password: TEST_AGENT_PASSWORD
    })
  });
  let cookie = sessionCookieFromResponse(setup);
  if (!cookie) {
    const login = await fetch(`http://127.0.0.1:${port}/auth/login`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ username: TEST_AGENT_USERNAME, password: TEST_AGENT_PASSWORD })
    });
    cookie = sessionCookieFromResponse(login);
    assert.equal(login.status, 200);
  } else {
    assert.equal(setup.status, 201);
  }
  assert.ok(cookie);
  const auth = { cookie };
  testAuthByPort.set(port, auth);
  return auth;
}

function sessionCookieFromResponse(response) {
  const value = response.headers.get("set-cookie") ?? "";
  return value.split(";")[0] || "";
}

export async function pollAgentResult(port, traceId) {
  let last = null;
  for (let attempt = 0; attempt < 20; attempt += 1) {
    const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat/result/${encodeURIComponent(traceId)}`);
    last = {
      status: response.status,
      body: await response.json()
    };
    if (response.status === 200) return last.body;
    await delay(10);
  }
  throw new Error(`Code Agent result did not complete: ${JSON.stringify(last)}`);
}

export async function createFakeCodexCommand() {
  const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-fake-codex-"));
  const packageRoot = path.join(root, "node_modules", "@openai", "codex");
  const command = path.join(packageRoot, "bin", "codex.js");
  const native = path.join(root, "node_modules", "@openai", "codex-linux-x64", "vendor", "x86_64-unknown-linux-musl", "codex", "codex");
  await mkdir(path.dirname(command), { recursive: true });
  await mkdir(path.dirname(native), { recursive: true });
  await writeFile(path.join(packageRoot, "package.json"), JSON.stringify({ name: "@openai/codex", version: "0.128.0" }), "utf8");
  await writeFile(command, "#!/usr/bin/env node\nconsole.log('codex 0.128.0');\n", "utf8");
  await writeFile(native, "#!/bin/sh\nexit 0\n", "utf8");
  await chmod(command, 0o755);
  await chmod(native, 0o755);
  return { root, command };
}

export async function prepareFakeCodexHome(codexHome) {
  await mkdir(codexHome, { recursive: true });
  await writeFile(path.join(codexHome, "config.toml"), "model = \"gpt-test\"\n", "utf8");
  await writeFile(path.join(codexHome, "auth.json"), "{\"auth\":\"redacted-test-placeholder\"}\n", "utf8");
  return codexHome;
}

export function createFakeAppServerClient({ text = "真实 Codex stdio 回复", delayMs = 0 } = {}) {
  let notificationHandler = null;
  return {
    async initialize() {
      return { initialized: true };
    },
    setNotificationHandler(handler) {
      notificationHandler = typeof handler === "function" ? handler : null;
    },
    async startThread() {
      notificationHandler?.({ method: "thread/started", params: { thread: { id: "thread_server_test_stdio" } } });
      return { threadId: "thread_server_test_stdio" };
    },
    async resumeThread(args) {
      notificationHandler?.({ method: "thread/started", params: { thread: { id: args.threadId } } });
      return { threadId: args.threadId };
    },
    async startTurn() {
      const turnId = "turn_server_test_stdio";
      notificationHandler?.({ method: "turn/started", params: { turn: { id: turnId } } });
      if (delayMs > 0) await delay(delayMs);
      notificationHandler?.({ method: "item/agentMessage/delta", params: { itemId: "item_server_test_stdio", delta: text } });
      notificationHandler?.({ method: "item/completed", params: { item: { id: "item_server_test_stdio", type: "agentMessage", text } } });
      notificationHandler?.({ method: "turn/completed", params: { turn: { id: turnId, status: "completed" } } });
      return { turnId };
    },
    close() {}
  };
}

export async function postAgentRaw(port, body, headers = {}) {
  const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      ...headers
    },
    body
  });
  return {
    status: response.status,
    body: await response.json()
  };
}

export function createGreenM3StatusRuntimeStore({ blocker = null } = {}) {
  return {
    async readiness() {
      return {
        adapter: "postgres",
        durable: true,
        durableRequested: true,
        durableCapable: true,
        ready: true,
        status: "ready",
        blocker,
        reason: "Postgres durable runtime adapter completed live schema, migration, and read readiness queries",
        liveRuntimeEvidence: true,
        fixtureEvidence: false
      };
    },
    async queryAuditEvents() {
      return {
        events: [
          {
            auditId: "aud_m3_di_read_d5afd84b-3a9d-475f-894f-dff8ea5f49ff_succeeded",
            traceId: "trc_hotfix_307_verify_1779527747043_read_false",
            actorType: "user",
            actorId: "usr_m3_operator",
            action: "m3.io.di.read",
            targetType: "hardware_operation",
            targetId: "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f",
            projectId: "prj_mvp_topology",
            gatewaySessionId: "gws_gwsimu_2",
            operationId: "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f",
            serviceId: "hwlab-cloud-api",
            outcome: "succeeded",
            metadata: {
              route: "res_boxsimu_1:DO1->hwlab-patch-panel->res_boxsimu_2:DI1"
            },
            environment: "dev",
            occurredAt: "2026-05-23T00:00:00.000Z"
          }
        ],
        count: 1
      };
    },
    async queryEvidenceRecords() {
      return {
        records: [
          {
            evidenceId: "evd_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f_succeeded",
            projectId: "prj_mvp_topology",
            operationId: "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f",
            kind: "trace",
            uri: "memory://hwlab-cloud-api/op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f/m3-io-control.json",
            sha256: "0".repeat(64),
            serviceId: "hwlab-cloud-api",
            environment: "dev",
            metadata: {
              traceId: "trc_hotfix_307_verify_1779527747043_read_false",
              route: "res_boxsimu_1:DO1->hwlab-patch-panel->res_boxsimu_2:DI1"
            },
            createdAt: "2026-05-23T00:00:00.000Z"
          }
        ],
        count: 1
      };
    }
  };
}
