import { readFileSync } from "node:fs";
import { basename, join } from "node:path";
import { z } from "zod";
import type { Adapter } from "../adapter";
import type { AdapterResult, Item } from "../schema";

export interface DeliveryAdapterOptions {
  id?: string;
  interval?: number;
  /** Absolute repo roots to check for a delivery receipt. */
  repos: string[];
  readFileImpl?: (path: string) => string;
}

// Written by finish-branch.sh `_write_delivery_receipt` after every merge-to-main land that
// declares a delivery kind. Absent file = project not yet enrolled under the delivery contract
// (not the same as an enrolled project that declared 'none' — that receipt exists, ok:true).
const DeliveryReceiptSchema = z.object({
  project: z.string().min(1),
  kind: z.enum(["none", "local-script", "push-triggered", "pr"]),
  ok: z.boolean(),
  reason: z.string(),
  at: z.string().min(1),
  sha: z.string(),
});
export type DeliveryReceipt = z.infer<typeof DeliveryReceiptSchema>;

const DEFAULT_INTERVAL_MS = 60_000;
const RECEIPT_REL = ".claude/delivery-receipt.json";

const KIND_LABEL: Record<DeliveryReceipt["kind"], string> = {
  none: "no deployment",
  "local-script": "local deploy",
  "push-triggered": "deploy on push",
  pr: "review queue",
};

function titleFor(receipt: DeliveryReceipt): string {
  const project = receipt.project;
  if (receipt.kind === "none") return `${project}: landed, no deployment declared`;
  return receipt.ok
    ? `${project}: delivered (${KIND_LABEL[receipt.kind]})`
    : `${project}: delivery failed (${KIND_LABEL[receipt.kind]})`;
}

function readReceipt(
  repoPath: string,
  readFileImpl: (path: string) => string,
): DeliveryReceipt | null {
  const path = join(repoPath, RECEIPT_REL);
  try {
    return DeliveryReceiptSchema.parse(JSON.parse(readFileImpl(path)));
  } catch (error) {
    if (error instanceof Error && "code" in error && (error as NodeJS.ErrnoException).code === "ENOENT") {
      return null;
    }
    console.warn(`[delivery] ignored unreadable ${path}: ${error instanceof Error ? error.message : String(error)}`);
    return null;
  }
}

/** Delivery outcome per enrolled project, from the receipt finish-branch.sh writes after landing. */
export function createDeliveryAdapter(opts: DeliveryAdapterOptions): Adapter {
  const id = opts.id ?? "delivery";
  const interval = opts.interval ?? DEFAULT_INTERVAL_MS;
  const repos = opts.repos;
  const readFileImpl = opts.readFileImpl ?? ((path: string) => readFileSync(path, "utf8"));

  async function poll(): Promise<AdapterResult> {
    const items: Item[] = [];
    for (const repoPath of repos) {
      const receipt = readReceipt(repoPath, readFileImpl);
      if (!receipt) continue;
      const repoName = basename(repoPath);
      items.push({
        id: `delivery:${repoName}`,
        source: id,
        project: receipt.project,
        severity: receipt.ok ? "info" : "act",
        kind: "build",
        title: titleFor(receipt),
        detail: receipt.ok ? receipt.reason : `${receipt.reason} (sha ${receipt.sha.slice(0, 12) || "unknown"})`,
        ts: receipt.at,
        actions: [],
      });
    }
    return { items, panels: [] };
  }

  return { id, interval, poll };
}
