import { describe, expect, test } from "bun:test";
import type { McpToolCaller } from "./mcp-tool-client.ts";
import {
  buildAiSchedulingProvisioningPlan,
  executeAiSchedulingExtensionProvisioning,
} from "./ringcentral-provisioning-plan.ts";

describe("RingCentral AI scheduling provisioning plan", () => {
  test("builds exact approval text and API extension action when extension is available", async () => {
    const plan = await buildAiSchedulingProvisioningPlan({ admin: new FakeAdmin() });

    expect(plan.ok).toBe(true);
    expect(plan.extensionNumberAvailable).toBe(true);
    expect(plan.canUseApiForExtensionCreate).toBe(true);
    expect(plan.approvalText).toContain("extension number '9001'");
    expect(plan.actions[0]?.endpoint).toBe("POST /restapi/v1.0/account/~/extension");
    expect(plan.actions[1]?.kind).toBe("manual_admin_console");
  });

  test("blocks when extension number is already assigned", async () => {
    const plan = await buildAiSchedulingProvisioningPlan({
      admin: new FakeAdmin({
        extensions: [{ id: 1, extensionNumber: "9001", name: "Someone Else" }],
      }),
    });

    expect(plan.ok).toBe(false);
    expect(plan.blockers[0]).toContain("already used");
  });

  test("blocks API creation when RingCentral app lacks EditAccounts", async () => {
    const plan = await buildAiSchedulingProvisioningPlan({
      admin: new FakeAdmin({ scopes: ["ReadAccounts", "ReadCallLog"] }),
    });

    expect(plan.ok).toBe(false);
    expect(plan.canUseApiForExtensionCreate).toBe(false);
    expect(plan.blockers).toContain("Current RingCentral JWT app does not advertise EditAccounts scope.");
  });

  test("dry-runs the gated extension create tool with exact approval text", async () => {
    const admin = new FakeAdmin();
    const plan = await buildAiSchedulingProvisioningPlan({ admin });
    const execution = await executeAiSchedulingExtensionProvisioning({
      admin,
      approvalText: plan.approvalText,
      execute: false,
    });

    expect(execution.ok).toBe(true);
    expect(execution.dryRun).toBe(true);
    expect(admin.createdDryRun).toBe(true);
  });

  test("refuses execution when approval text differs", async () => {
    const execution = await executeAiSchedulingExtensionProvisioning({
      admin: new FakeAdmin(),
      approvalText: "approved-ish",
      execute: true,
    });

    expect(execution.ok).toBe(false);
    expect(execution.plan.blockers).toContain(
      "Provided approval text does not exactly match the checked plan.",
    );
  });
});

class FakeAdmin implements McpToolCaller {
  createdDryRun = false;

  constructor(
    private readonly opts: {
      extensions?: Array<Record<string, unknown>>;
      scopes?: string[];
    } = {},
  ) {}

  async callTool(name: string): Promise<unknown> {
    if (name === "list_extensions") {
      return { records: this.opts.extensions ?? [] };
    }
    if (name === "get_auth_context") {
      const scopes = this.opts.scopes ?? ["ReadAccounts", "EditAccounts", "EditExtensions"];
      return {
        scopes,
        has_edit_accounts: scopes.includes("EditAccounts"),
        has_edit_extensions: scopes.includes("EditExtensions"),
      };
    }
    if (name === "create_ai_scheduling_extension") {
      this.createdDryRun = true;
      return { ok: true, dry_run: true };
    }
    throw new Error(`unexpected tool ${name}`);
  }

  async listTools(): Promise<string[]> {
    return [];
  }

  async close(): Promise<void> {}
}
