import { randomUUID } from "node:crypto";
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";

import type { UniDeskConfig } from "../config";
import { startJob } from "../jobs";

import { apply, plan, status } from "./actions";
import { readSub2ApiConfig } from "./config";
import type { Sub2ApiTargetConfig } from "./entry";
import { imagePrepull } from "./image-prepull";
import { imageRef, resolveTarget } from "./manifest";
import type { ApplyOptions } from "./options";
import { validate } from "./policy";

type JsonRecord = Record<string, unknown>;

function record(value: unknown): JsonRecord {
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value as JsonRecord : {};
}

function normalizedUrl(value: string): string {
  return value.replace(/\/+$/u, "");
}

function skippedSmoke(reason: string, detail?: string): JsonRecord {
  return {
    ok: true,
    executed: false,
    status: "skipped",
    reason,
    detail: detail ?? null,
    valuesPrinted: false,
  };
}

async function existingConsumerSmoke(target: Sub2ApiTargetConfig): Promise<JsonRecord> {
  if (target.publicExposure === null || !target.publicExposure.enabled) return skippedSmoke("public-exposure-disabled");
  const configFile = join(homedir(), ".codex", "config.toml");
  const authFile = join(homedir(), ".codex", "auth.json");
  if (!existsSync(configFile) || !existsSync(authFile)) return skippedSmoke("existing-consumer-config-missing");

  let consumer: JsonRecord;
  let auth: JsonRecord;
  try {
    consumer = record(Bun.TOML.parse(readFileSync(configFile, "utf8")));
    auth = record(JSON.parse(readFileSync(authFile, "utf8")) as unknown);
  } catch {
    return { ok: false, executed: false, status: "failed", reason: "existing-consumer-config-invalid", valuesPrinted: false };
  }

  const providerName = typeof consumer.model_provider === "string" ? consumer.model_provider : null;
  const providers = record(consumer.model_providers);
  const provider = providerName === null ? {} : record(providers[providerName]);
  const baseUrl = typeof provider.base_url === "string" ? provider.base_url : null;
  const model = typeof consumer.model === "string" ? consumer.model : null;
  const apiKey = typeof auth.OPENAI_API_KEY === "string" ? auth.OPENAI_API_KEY : null;
  if (baseUrl === null || model === null || apiKey === null || apiKey.length === 0) {
    return { ok: false, executed: false, status: "failed", reason: "existing-consumer-config-incomplete", valuesPrinted: false };
  }
  if (normalizedUrl(baseUrl) !== normalizedUrl(target.publicExposure.publicBaseUrl)) {
    return skippedSmoke("existing-consumer-target-mismatch", `${normalizedUrl(baseUrl)} != ${normalizedUrl(target.publicExposure.publicBaseUrl)}`);
  }

  const sub2api = readSub2ApiConfig();
  const requestId = randomUUID();
  const startedAt = Date.now();
  try {
    const response = await fetch(`${normalizedUrl(baseUrl)}/v1/responses`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "X-Request-ID": requestId,
        "OpenAI-Client-Request-ID": requestId,
      },
      body: JSON.stringify({
        model,
        input: "Reply OK.",
        stream: false,
        store: false,
        max_output_tokens: sub2api.defaults.rollout.smokeMaxOutputTokens,
      }),
      signal: AbortSignal.timeout(sub2api.defaults.rollout.smokeTimeoutSeconds * 1000),
    });
    const body = record(await response.json().catch(() => ({})));
    const responseStatus = typeof body.status === "string" ? body.status : null;
    const responseId = typeof body.id === "string" ? body.id : null;
    const error = record(body.error);
    const errorType = typeof error.type === "string" ? error.type : null;
    return {
      ok: response.ok && responseStatus === "completed",
      executed: true,
      status: response.ok && responseStatus === "completed" ? "passed" : "failed",
      requestId,
      httpStatus: response.status,
      responseId,
      responseStatus,
      errorType,
      elapsedMs: Date.now() - startedAt,
      valuesPrinted: false,
    };
  } catch (error) {
    return {
      ok: false,
      executed: true,
      status: "failed",
      requestId,
      httpStatus: null,
      responseId: null,
      responseStatus: null,
      errorType: error instanceof Error ? error.name : "request-failed",
      elapsedMs: Date.now() - startedAt,
      valuesPrinted: false,
    };
  }
}

export async function rolloutSmoke(targetId: string): Promise<JsonRecord> {
  const sub2api = readSub2ApiConfig();
  const target = resolveTarget(sub2api, targetId);
  return {
    action: "platform-infra-sub2api-smoke",
    target: { id: target.id, route: target.route, namespace: target.namespace },
    ...await existingConsumerSmoke(target),
  };
}

function stageOk(value: JsonRecord): boolean {
  return value.ok === true;
}

function runtimeImages(statusResult: JsonRecord): string[] {
  const summary = record(statusResult.summary);
  const containerImages = Array.isArray(summary.containers)
    ? summary.containers.map((item) => record(item)).map((item) => item.Image ?? item.image).filter((item): item is string => typeof item === "string")
    : [];
  const deploymentImages = Array.isArray(summary.deployments)
    ? summary.deployments.map((item) => record(item)).flatMap((item) => Array.isArray(item.images) ? item.images.filter((image): image is string => typeof image === "string") : [])
    : [];
  return Array.from(new Set([...containerImages, ...deploymentImages]));
}

function emitRolloutProgress(targetId: string, stage: "prepull" | "apply" | "verify", status: "started" | "succeeded" | "failed", detail: JsonRecord = {}): void {
  process.stderr.write(`${JSON.stringify({
    event: "platform-infra.sub2api.rollout.progress",
    at: new Date().toISOString(),
    targetId,
    stage,
    status,
    ...detail,
  })}\n`);
}

export async function rollout(config: UniDeskConfig, options: ApplyOptions): Promise<JsonRecord> {
  const sub2api = readSub2ApiConfig();
  const target = resolveTarget(sub2api, options.targetId);
  const desiredImage = imageRef(sub2api, target);
  if (options.confirm && !options.wait) {
    const job = startJob(
      `platform_infra_sub2api_rollout_${target.id.toLowerCase()}`,
      ["bun", "scripts/cli.ts", "platform-infra", "sub2api", "rollout", "--target", target.id, "--confirm", "--wait"],
      `Roll out and verify ${target.id} Sub2API image from YAML-controlled desired state`,
    );
    const statusCommand = `bun scripts/cli.ts job status ${job.id} --tail-bytes 12000`;
    return {
      ok: true,
      action: "platform-infra-sub2api-rollout",
      mode: "async-job",
      target: { id: target.id, route: target.route, namespace: target.namespace },
      desiredImage,
      job,
      statusCommand,
      next: { status: statusCommand },
    };
  }

  const startedAt = Date.now();
  if (!options.confirm) {
    const desiredPlan = plan({ targetId: target.id });
    const current = {
      ok: true,
      action: "platform-infra-sub2api-current-deferred",
      mode: "deferred-to-confirm-verify",
      mutation: false,
    };
    const [prepull, applyDryRun] = await Promise.all([
      imagePrepull(config, { ...options, dryRun: true, confirm: false, wait: true }),
      apply(config, { ...options, dryRun: true, confirm: false, wait: true }),
    ]);
    return {
      ok: stageOk(desiredPlan) && stageOk(current) && stageOk(prepull) && stageOk(applyDryRun),
      action: "platform-infra-sub2api-rollout",
      mode: "dry-run",
      target: { id: target.id, route: target.route, namespace: target.namespace },
      desiredImage,
      stages: { plan: desiredPlan, current, prepull, apply: applyDryRun },
      timingsMs: { total: Date.now() - startedAt },
      mutation: false,
    };
  }

  const prepullStartedAt = Date.now();
  emitRolloutProgress(target.id, "prepull", "started");
  let prepull: JsonRecord;
  try {
    prepull = await imagePrepull(config, { ...options, dryRun: false, confirm: true, wait: true });
  } catch (error) {
    emitRolloutProgress(target.id, "prepull", "failed", { elapsedMs: Date.now() - prepullStartedAt, errorType: error instanceof Error ? error.name : "unknown" });
    throw error;
  }
  const prepullMs = Date.now() - prepullStartedAt;
  emitRolloutProgress(target.id, "prepull", stageOk(prepull) ? "succeeded" : "failed", { elapsedMs: prepullMs });
  if (!stageOk(prepull)) {
    return {
      ok: false,
      action: "platform-infra-sub2api-rollout",
      mode: "confirmed",
      target: { id: target.id, route: target.route, namespace: target.namespace },
      desiredImage,
      failedStage: "prepull",
      stages: { prepull },
      timingsMs: { prepull: prepullMs, total: Date.now() - startedAt },
      mutation: true,
    };
  }

  const applyStartedAt = Date.now();
  emitRolloutProgress(target.id, "apply", "started");
  let applied: JsonRecord;
  try {
    applied = await apply(config, { ...options, dryRun: false, confirm: true, wait: true });
  } catch (error) {
    emitRolloutProgress(target.id, "apply", "failed", { elapsedMs: Date.now() - applyStartedAt, errorType: error instanceof Error ? error.name : "unknown" });
    throw error;
  }
  const applyMs = Date.now() - applyStartedAt;
  emitRolloutProgress(target.id, "apply", stageOk(applied) ? "succeeded" : "failed", { elapsedMs: applyMs });
  if (!stageOk(applied)) {
    return {
      ok: false,
      action: "platform-infra-sub2api-rollout",
      mode: "confirmed",
      target: { id: target.id, route: target.route, namespace: target.namespace },
      desiredImage,
      failedStage: "apply",
      stages: { prepull, apply: applied },
      timingsMs: { prepull: prepullMs, apply: applyMs, total: Date.now() - startedAt },
      mutation: true,
    };
  }

  const verifyStartedAt = Date.now();
  emitRolloutProgress(target.id, "verify", "started");
  let current: JsonRecord;
  let validation: JsonRecord;
  let smoke: JsonRecord;
  try {
    [current, validation, smoke] = await Promise.all([
      status(config, { targetId: target.id, full: false, raw: false }),
      validate(config, { targetId: target.id, full: false, raw: false }),
      rolloutSmoke(target.id),
    ]);
  } catch (error) {
    emitRolloutProgress(target.id, "verify", "failed", { elapsedMs: Date.now() - verifyStartedAt, errorType: error instanceof Error ? error.name : "unknown" });
    throw error;
  }
  const verifyMs = Date.now() - verifyStartedAt;
  const observedImages = runtimeImages(current);
  const imageAlignment = {
    ok: observedImages.includes(desiredImage),
    desiredImage,
    observedImages,
  };
  const verificationOk = stageOk(current) && stageOk(validation) && stageOk(smoke) && imageAlignment.ok;
  emitRolloutProgress(target.id, "verify", verificationOk ? "succeeded" : "failed", {
    elapsedMs: verifyMs,
    checks: {
      status: stageOk(current),
      image: imageAlignment.ok,
      validate: stageOk(validation),
      smoke: stageOk(smoke),
    },
  });
  return {
    ok: verificationOk,
    action: "platform-infra-sub2api-rollout",
    mode: "confirmed",
    target: { id: target.id, route: target.route, namespace: target.namespace },
    desiredImage,
    failedStage: verificationOk ? null : "verify",
    stages: { prepull, apply: applied, current, image: imageAlignment, validate: validation, smoke },
    timingsMs: {
      prepull: prepullMs,
      apply: applyMs,
      verify: verifyMs,
      total: Date.now() - startedAt,
    },
    mutation: true,
  };
}
