import { createConnection } from "node:net";

import { MockAmapAdapter, RealAmapAdapter } from "./amap-adapter.js";

const API_CREDENTIALS = ["Dada/P0A/api/resend", "Dada/P0A/api/amap", "Dada/P0A/admin/pepper"] as const;

export async function receiveApiCredentials(input: NodeJS.ReadableStream = process.stdin) {
  const chunks: Buffer[] = [];
  for await (const chunk of input) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
  const payload = Buffer.concat(chunks);
  try {
    const parsed = JSON.parse(payload.toString("utf8")) as Record<string, unknown>;
    const names = Object.keys(parsed).sort();
    const expected = [...API_CREDENTIALS].sort();
    if (names.length !== expected.length || names.some((name, index) => name !== expected[index])) {
      throw new Error("API credential channel contains an unexpected credential scope.");
    }
    if (expected.some((name) => typeof parsed[name] !== "string")) {
      throw new Error("API credential channel contains an invalid credential value.");
    }
    return parsed as Record<(typeof API_CREDENTIALS)[number], string>;
  } finally {
    payload.fill(0);
    for (const chunk of chunks) chunk.fill(0);
    chunks.length = 0;
  }
}

export function initializeApiCredentialClients(credentials: Record<(typeof API_CREDENTIALS)[number], string>) {
  try {
    const adminPepper = credentials["Dada/P0A/admin/pepper"];
    if (!adminPepper) throw new Error("admin_pepper_not_configured");
    return {
      adminAllowlistPepper: Buffer.from(adminPepper, "utf8"),
      amap: credentials["Dada/P0A/api/amap"] ? new RealAmapAdapter(credentials["Dada/P0A/api/amap"]) : new MockAmapAdapter(),
      resendConfigured: Boolean(credentials["Dada/P0A/api/resend"]),
    };
  } finally {
    for (const name of API_CREDENTIALS) credentials[name] = "";
  }
}

export function attachApiSupervisorControl(pipeName: string, shutdown: () => Promise<void>) {
  const socket = createConnection(`\\\\.\\pipe\\${pipeName}`);
  let pending = "";
  let connected = false;
  const pendingStatuses: string[] = [];
  socket.setEncoding("utf8");
  socket.on("connect", () => {
    connected = true;
    socket.write("ready\n");
    for (const status of pendingStatuses.splice(0)) socket.write(`${status}\n`);
  });
  socket.on("data", (chunk) => {
    pending += chunk;
    if (!pending.includes("\n")) return;
    const [command] = pending.split("\n", 1);
    pending = "";
    if (command === "shutdown") void shutdown().finally(() => socket.end());
  });
  return {
    reportStatus(status: "storage_unavailable") {
      if (socket.destroyed) return;
      if (connected) socket.write(`${status}\n`); else pendingStatuses.push(status);
    },
    socket,
  };
}
