import { describe, it, expect, vi, beforeEach } from "vitest";

vi.mock("../auth/google-integration.ts", () => ({
  fetchGoogleAccountEmail: vi.fn(),
  storeGoogleAccount: vi.fn(),
}));
vi.mock("../db/integrations.ts", () => ({
  upsertIntegration: vi.fn(),
  removeIntegration: vi.fn().mockResolvedValue(null),
}));
vi.mock("../auth/slack-oauth.ts", () => ({
  storeSlackOAuth: vi.fn(),
}));

import { depositOAuthCredential } from "./oauth-deposit.ts";
import { fetchGoogleAccountEmail, storeGoogleAccount } from "../auth/google-integration.ts";
import { upsertIntegration } from "../db/integrations.ts";
import { storeSlackOAuth } from "../auth/slack-oauth.ts";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function makeCall(request: Record<string, unknown>): any {
  return { request, metadata: { get: () => [] } };
}

describe("depositOAuthCredential", () => {
  beforeEach(() => vi.clearAllMocks());

  it("routes a google deposit through storeGoogleAccount (canonical, not the generic row)", async () => {
    (fetchGoogleAccountEmail as ReturnType<typeof vi.fn>).mockResolvedValue("me@x.com");
    const cb = vi.fn();
    await depositOAuthCredential(
      makeCall({
        provider: "google",
        userId: "u1",
        accessToken: "at",
        refreshToken: "rt",
        expiresAt: 123,
        scopes: "calendar",
        metadata: {},
      }),
      cb,
    );
    // The Google MCP builder reads google:{userId}:{email} via storeGoogleAccount;
    // the generic provider:userId upsert (invisible to it) must NOT be used.
    expect(storeGoogleAccount).toHaveBeenCalledWith(
      expect.objectContaining({
        userId: "u1",
        email: "me@x.com",
        tokens: expect.objectContaining({ accessToken: "at", refreshToken: "rt", expiresAt: 123 }),
      }),
    );
    expect(upsertIntegration).not.toHaveBeenCalled();
    expect(cb).toHaveBeenCalledWith(null, expect.objectContaining({ success: true }));
  });

  it("fails the google deposit when the email can't be resolved (no broken row)", async () => {
    (fetchGoogleAccountEmail as ReturnType<typeof vi.fn>).mockResolvedValue(undefined);
    const cb = vi.fn();
    await depositOAuthCredential(
      makeCall({
        provider: "google",
        userId: "u1",
        accessToken: "at",
        refreshToken: "",
        expiresAt: 0,
        scopes: "",
        metadata: {},
      }),
      cb,
    );
    expect(storeGoogleAccount).not.toHaveBeenCalled();
    expect(upsertIntegration).not.toHaveBeenCalled();
    expect(cb).toHaveBeenCalledWith(
      expect.objectContaining({ message: expect.stringContaining("email") }),
    );
  });

  it("writes a generic provider:userId row for non-canonical providers", async () => {
    const cb = vi.fn();
    await depositOAuthCredential(
      makeCall({
        provider: "notion",
        userId: "u1",
        accessToken: "at",
        refreshToken: "rt",
        expiresAt: 1,
        scopes: "read_content",
        metadata: {},
      }),
      cb,
    );
    expect(upsertIntegration).toHaveBeenCalledWith(
      "notion:u1",
      expect.objectContaining({
        enabled: true,
        secrets: expect.objectContaining({ access_token: "at" }),
      }),
    );
    expect(storeGoogleAccount).not.toHaveBeenCalled();
    expect(cb).toHaveBeenCalledWith(null, expect.objectContaining({ success: true }));
  });

  it("routes a slack deposit through storeSlackOAuth (canonical slack-oauth row, not the generic one)", async () => {
    const cb = vi.fn();
    await depositOAuthCredential(
      makeCall({
        provider: "slack",
        userId: "u1",
        accessToken: "xoxp-tok",
        refreshToken: "",
        expiresAt: 0,
        scopes: "channels:read,chat:write",
        metadata: { team_id: "T1", team_name: "Acme", slack_user_id: "U1" },
      }),
      cb,
    );
    // getSlackMcpToken reads slack-oauth:{userId}; the generic slack:{userId} row is
    // invisible to it, so the deposit must land via storeSlackOAuth instead.
    expect(storeSlackOAuth).toHaveBeenCalledWith(
      "u1",
      expect.objectContaining({
        accessToken: "xoxp-tok",
        teamId: "T1",
        teamName: "Acme",
        userId: "U1",
      }),
    );
    expect(upsertIntegration).not.toHaveBeenCalledWith("slack:u1", expect.anything());
    expect(storeGoogleAccount).not.toHaveBeenCalled();
    expect(cb).toHaveBeenCalledWith(null, expect.objectContaining({ success: true }));
  });
});
