import { afterEach, beforeEach, vi } from "vitest";
import { withTempHome as withTempHomeBase } from "../../test/helpers/temp-home.js";
import type { OpenClawConfig } from "../config/config.js";
import type { MockFn } from "../test-utils/vitest-mock-fn.js";
import { resolveImplicitProviders } from "./models-config.providers.implicit.js";

export function withModelsTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
  return withTempHomeBase(fn, { prefix: "openclaw-models-" });
}

export function installModelsConfigTestHooks(opts?: { restoreFetch?: boolean }) {
  let previousHome: string | undefined;
  const originalFetch = globalThis.fetch;

  beforeEach(() => {
    previousHome = process.env.HOME;
  });

  afterEach(() => {
    process.env.HOME = previousHome;
    if (opts?.restoreFetch && originalFetch) {
      globalThis.fetch = originalFetch;
    }
  });
}

export async function withTempEnv<T>(vars: string[], fn: () => Promise<T>): Promise<T> {
  const previous: Record<string, string | undefined> = {};
  for (const envVar of vars) {
    previous[envVar] = process.env[envVar];
  }

  try {
    return await fn();
  } finally {
    for (const envVar of vars) {
      const value = previous[envVar];
      if (value === undefined) {
        delete process.env[envVar];
      } else {
        process.env[envVar] = value;
      }
    }
  }
}

export function unsetEnv(vars: string[]) {
  for (const envVar of vars) {
    delete process.env[envVar];
  }
}

export const COPILOT_TOKEN_ENV_VARS = ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"];

export async function withUnsetCopilotTokenEnv<T>(fn: () => Promise<T>): Promise<T> {
  return withTempEnv(COPILOT_TOKEN_ENV_VARS, async () => {
    unsetEnv(COPILOT_TOKEN_ENV_VARS);
    return fn();
  });
}

export function mockCopilotTokenExchangeSuccess(): MockFn {
  const fetchMock = vi.fn().mockResolvedValue({
    ok: true,
    status: 200,
    json: async () => ({
      token: "copilot-token;proxy-ep=proxy.copilot.example",
      expires_at: Math.floor(Date.now() / 1000) + 3600,
    }),
  });
  globalThis.fetch = fetchMock as unknown as typeof fetch;
  return fetchMock;
}

export async function withCopilotGithubToken<T>(
  token: string,
  fn: (fetchMock: MockFn) => Promise<T>,
): Promise<T> {
  return withTempEnv(["COPILOT_GITHUB_TOKEN"], async () => {
    process.env.COPILOT_GITHUB_TOKEN = token;
    const fetchMock = mockCopilotTokenExchangeSuccess();
    return fn(fetchMock);
  });
}

export const MODELS_CONFIG_IMPLICIT_ENV_VARS = [
  "VITEST",
  "NODE_ENV",
  "AI_GATEWAY_API_KEY",
  "CLOUDFLARE_AI_GATEWAY_API_KEY",
  "COPILOT_GITHUB_TOKEN",
  "GH_TOKEN",
  "GITHUB_TOKEN",
  "HF_TOKEN",
  "HUGGINGFACE_HUB_TOKEN",
  "MINIMAX_API_KEY",
  "MINIMAX_OAUTH_TOKEN",
  "MOONSHOT_API_KEY",
  "NVIDIA_API_KEY",
  "OLLAMA_API_KEY",
  "OPENCLAW_AGENT_DIR",
  "OPENAI_API_KEY",
  "OPENROUTER_API_KEY",
  "PI_CODING_AGENT_DIR",
  "QIANFAN_API_KEY",
  "MODELSTUDIO_API_KEY",
  "SYNTHETIC_API_KEY",
  "TOGETHER_API_KEY",
  "VOLCANO_ENGINE_API_KEY",
  "BYTEPLUS_API_KEY",
  "KILOCODE_API_KEY",
  "KIMI_API_KEY",
  "KIMICODE_API_KEY",
  "GEMINI_API_KEY",
  "GOOGLE_APPLICATION_CREDENTIALS",
  "GOOGLE_CLOUD_LOCATION",
  "GOOGLE_CLOUD_PROJECT",
  "GOOGLE_CLOUD_PROJECT_ID",
  "ANTHROPIC_VERTEX_USE_GCP_METADATA",
  "VENICE_API_KEY",
  "VLLM_API_KEY",
  "XIAOMI_API_KEY",
  "ANTHROPIC_VERTEX_PROJECT_ID",
  "CLOUD_ML_REGION",
  // Avoid ambient AWS creds unintentionally enabling Bedrock discovery.
  "AWS_ACCESS_KEY_ID",
  "AWS_CONFIG_FILE",
  "AWS_BEARER_TOKEN_BEDROCK",
  "AWS_DEFAULT_REGION",
  "AWS_PROFILE",
  "AWS_REGION",
  "AWS_SESSION_TOKEN",
  "AWS_SECRET_ACCESS_KEY",
  "AWS_SHARED_CREDENTIALS_FILE",
];

export function snapshotImplicitProviderEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
  const source = env ?? process.env;
  const snapshot: NodeJS.ProcessEnv = {};

  for (const envVar of MODELS_CONFIG_IMPLICIT_ENV_VARS) {
    const value = source[envVar];
    if (value !== undefined) {
      snapshot[envVar] = value;
    }
  }

  return snapshot;
}

export function resolveImplicitProvidersForTest(
  params: Parameters<typeof resolveImplicitProviders>[0],
) {
  return resolveImplicitProviders({
    ...params,
    env: snapshotImplicitProviderEnv(params.env),
  });
}

export const CUSTOM_PROXY_MODELS_CONFIG: OpenClawConfig = {
  models: {
    providers: {
      "custom-proxy": {
        baseUrl: "http://localhost:4000/v1",
        apiKey: "TEST_KEY",
        api: "openai-completions",
        models: [
          {
            id: "llama-3.1-8b",
            name: "Llama 3.1 8B (Proxy)",
            api: "openai-completions",
            reasoning: false,
            input: ["text"],
            cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
            contextWindow: 128000,
            maxTokens: 32000,
          },
        ],
      },
    },
  },
};
