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

const syncOAuthRuntimeState = vi.fn(async () => {});
const getOAuthTokens = vi.fn(async () => ({
  access: "access-token",
  refresh: "refresh-token",
  type: "oauth" as const,
}));
const removeOAuthTokens = vi.fn(async () => {});
const removeUserInfo = vi.fn(async () => {});
const revokeNextApiOAuthToken = vi.fn(async () => {});
const loadDimcodeUserConfig = vi.fn(async () => ({
  config: { version: 1 as const, updatedAt: 0 },
  dir: "/tmp/dimcode",
  filePath: "/tmp/dimcode/config.json",
}));

vi.mock("#cli/core/auth", () => ({
  OAUTH_DUMMY_KEY: "dimcode-oauth-dummy-key",
  getOAuthTokens,
  loadUserInfo: vi.fn(async () => undefined),
  removeOAuthTokens,
  removeUserInfo,
  saveUserInfo: vi.fn(async () => {}),
  setOAuthTokens: vi.fn(async () => {}),
}));

vi.mock("#cli/core/next-api-oauth", () => ({
  NEXT_API_OAUTH_ISSUER: "https://nextapi.example.test",
  NEXT_API_OAUTH_PROVIDER_ID: "next-api-oauth",
  buildNextApiOAuthTokenRecord: vi.fn(),
  describeNextApiOAuthConfigError: vi.fn(() => ""),
  fetchNextApiOAuthModels: vi.fn(async () => []),
  fetchNextApiOAuthUserInfo: vi.fn(async () => undefined),
  resolveNextApiOAuthBrowserUrl: vi.fn(() => "https://nextapi.example.test/login"),
  resolveNextApiOAuthConfig: vi.fn(async () => ({
    authorizationEndpoint: "https://nextapi.example.test/oauth/authorize",
    clientId: "client-id",
    issuer: "https://nextapi.example.test",
    port: 1456,
    relayBaseUrl: "https://nextapi.example.test/v1",
    scope: "openid profile email",
    tokenEndpoint: "https://nextapi.example.test/oauth/token",
  })),
  revokeNextApiOAuthToken,
  startNextApiOAuthServer: vi.fn(async () => ({ redirectUri: "http://127.0.0.1:1456/callback" })),
  stopNextApiOAuthServer: vi.fn(async () => {}),
  waitForNextApiOAuthCallback: vi.fn(async () => ({
    access_token: "access-token",
    refresh_token: "refresh-token",
  })),
}));

vi.mock("#cli/core/oauth", () => ({
  extractEmail: vi.fn(() => undefined),
  generatePKCE: vi.fn(async () => ({ codeChallenge: "challenge", codeVerifier: "verifier" })),
  generateState: vi.fn(() => "state"),
}));

vi.mock("#cli/core/oauth-runtime-sync", () => ({
  syncOAuthRuntimeState,
}));

vi.mock("../src/dimsdk/cli/user-cache.ts", () => ({
  loadDimcodeUserConfig,
}));

const { useNextApiOAuthDialog } = await import("../src/dimsdk/pages/utils/next-api-oauth-dialog.ts");

afterEach(() => {
  syncOAuthRuntimeState.mockClear();
  getOAuthTokens.mockClear();
  removeOAuthTokens.mockClear();
  removeUserInfo.mockClear();
  revokeNextApiOAuthToken.mockClear();
  loadDimcodeUserConfig.mockClear();
});

describe("next api oauth dialog", () => {
  it("clears primary runtime state based on the active provider without the legacy env flag", async () => {
    const toasts: string[] = [];
    const store = {
      state: {
        config: {},
        context: { env: {} },
        provider: { activeId: "next-api-oauth" },
      },
      setEnvVar: vi.fn(),
    };

    const dialog = useNextApiOAuthDialog({
      nodeLike: false,
      showToast: (text: string) => {
        toasts.push(text);
      },
      store,
    });

    await dialog.logoutNextApiOAuth();

    expect(revokeNextApiOAuthToken).toHaveBeenCalled();
    expect(removeOAuthTokens).toHaveBeenCalledWith("next-api-oauth");
    expect(removeUserInfo).toHaveBeenCalled();
    expect(syncOAuthRuntimeState).toHaveBeenCalledWith(
      expect.objectContaining({
        clearApiKey: true,
        clearBaseUrl: true,
        clearModel: true,
        clearProviderId: true,
      }),
    );
    expect(toasts.length).toBeGreaterThanOrEqual(0);
  });

  it("clears primary runtime state when the active base url matches the persisted oauth connection", async () => {
    const store = {
      state: {
        config: {
          baseUrl: "https://nextapi.example.test/v1",
        },
        context: { env: {} },
        provider: { activeId: "openai" },
      },
      setEnvVar: vi.fn(),
    };

    loadDimcodeUserConfig.mockResolvedValueOnce({
      config: {
        version: 1 as const,
        updatedAt: 0,
        settings: {
          providerConnections: {
            "next-api-oauth": {
              baseUrl: "https://nextapi.example.test/v1/",
              models: [
                { id: "next-api-model", name: "Next API Model" },
              ],
            },
          },
          activeModelByProvider: {
            "next-api-oauth": "next-api-model",
          },
        },
      },
      dir: "/tmp/dimcode",
      filePath: "/tmp/dimcode/config.json",
    });

    const dialog = useNextApiOAuthDialog({
      nodeLike: false,
      showToast: () => {},
      store,
    });

    await dialog.logoutNextApiOAuth();

    expect(loadDimcodeUserConfig).toHaveBeenCalled();
    expect(syncOAuthRuntimeState).toHaveBeenLastCalledWith(
      expect.objectContaining({
        clearApiKey: true,
        clearBaseUrl: true,
        clearModel: true,
        clearProviderId: true,
      }),
    );
  });
});
