import { classifyNetworkFailure, type NetworkFailureCategory } from "./networkFailure.js";

export interface FeishuReadinessInput {
  readonly appId: string;
  readonly appSecret: string;
  readonly botOpenId: string;
}

export type FeishuReadinessProbe = (input: FeishuReadinessInput) => Promise<void>;

export class FeishuReadinessError extends Error {
  constructor(
    readonly code: "feishu_readiness_unreachable" | "feishu_readiness_rejected",
    message: string,
    readonly category: NetworkFailureCategory | "http" | "provider_rejection",
    readonly upstreamStatus?: number,
  ) {
    super(message);
    this.name = "FeishuReadinessError";
  }
}

/** Obtain a tenant token, prove the configured bot identity, then discard it. */
export async function probeFeishuApplication(input: FeishuReadinessInput): Promise<void> {
  const response = await feishuFetch(
    "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
    {
      method: "POST",
      headers: { "content-type": "application/json", accept: "application/json" },
      body: JSON.stringify({ app_id: input.appId, app_secret: input.appSecret }),
      redirect: "manual",
      signal: AbortSignal.timeout(10_000),
    },
  );
  if (!response.ok) {
    await response.body?.cancel();
    throw new FeishuReadinessError(
      "feishu_readiness_rejected",
      `Feishu application readiness check failed: status ${response.status}`,
      "http",
      response.status,
    );
  }
  let payload: unknown;
  try {
    payload = await response.json();
  } catch {
    throw new FeishuReadinessError(
      "feishu_readiness_rejected",
      "Feishu application readiness check returned an invalid response",
      "provider_rejection",
    );
  }
  if (!isRecord(payload) || payload["code"] !== 0 ||
      typeof payload["tenant_access_token"] !== "string" || payload["tenant_access_token"] === "") {
    throw new FeishuReadinessError(
      "feishu_readiness_rejected",
      "Feishu application credentials were rejected",
      "provider_rejection",
    );
  }
  const botResponse = await feishuFetch("https://open.feishu.cn/open-apis/bot/v3/info", {
    method: "GET",
    headers: {
      accept: "application/json",
      authorization: `Bearer ${payload["tenant_access_token"]}`,
    },
    redirect: "manual",
    signal: AbortSignal.timeout(10_000),
  });
  if (!botResponse.ok) {
    await botResponse.body?.cancel();
    throw new FeishuReadinessError(
      "feishu_readiness_rejected",
      `Feishu bot readiness check failed: status ${botResponse.status}`,
      "http",
      botResponse.status,
    );
  }
  let botPayload: unknown;
  try {
    botPayload = await botResponse.json();
  } catch {
    throw new FeishuReadinessError(
      "feishu_readiness_rejected",
      "Feishu bot readiness check returned an invalid response",
      "provider_rejection",
    );
  }
  if (!isRecord(botPayload) || botPayload["code"] !== 0 ||
      !isRecord(botPayload["bot"]) || botPayload["bot"]["open_id"] !== input.botOpenId) {
    throw new FeishuReadinessError(
      "feishu_readiness_rejected",
      "Feishu bot identity does not match the configured botOpenId",
      "provider_rejection",
    );
  }
}

async function feishuFetch(url: string, init: RequestInit): Promise<Response> {
  try {
    return await fetch(url, init);
  } catch (error) {
    throw new FeishuReadinessError(
      "feishu_readiness_unreachable",
      "Feishu application readiness check could not reach provider",
      classifyNetworkFailure(error),
    );
  }
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
