/** Tests model discovery auth storage, SecretRef placeholders, and env-backed credentials. */
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { MAX_DATE_TIMESTAMP_MS } from "@openclaw/normalization-core/number-coercion";
import { describe, expect, it, vi } from "vitest";
import { resolveAgentCredentialMapFromStore } from "./agent-auth-credentials.js";
import { addEnvBackedAgentCredentials } from "./agent-auth-discovery-core.js";
import { discoverAuthStorage } from "./agent-model-discovery.js";
import type { AuthProfileStore } from "./auth-profiles.js";
import { writePersistedAuthProfileStoreRaw } from "./auth-profiles/sqlite.js";

vi.mock("./model-auth-env-vars.js", () => ({
  listProviderEnvAuthLookupKeys: () => ["mistral", "workspace-cloud"],
  resolveProviderEnvAuthLookupMaps: () => ({
    aliasMap: {},
    envCandidateMap: {
      mistral: ["MISTRAL_API_KEY"],
    },
    authEvidenceMap: {
      "workspace-cloud": [
        {
          type: "local-file-with-env",
          credentialMarker: "workspace-cloud-local-credentials",
          source: "workspace cloud credentials",
        },
      ],
    },
  }),
}));

vi.mock("./model-auth-env.js", () => ({
  resolveEnvApiKey: (
    provider: string,
    env: NodeJS.ProcessEnv,
    options?: { workspaceDir?: string },
  ) => {
    if (provider === "mistral" && env.MISTRAL_API_KEY?.trim()) {
      return { apiKey: env.MISTRAL_API_KEY, source: "env: MISTRAL_API_KEY" };
    }
    if (provider === "workspace-cloud" && options?.workspaceDir === "/tmp/workspace") {
      return {
        apiKey: "workspace-cloud-local-credentials",
        source: "workspace cloud credentials",
      };
    }
    return null;
  },
}));

async function createAgentDir(): Promise<string> {
  return await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-agent-auth-storage-"));
}

async function withAgentDir(run: (agentDir: string) => Promise<void>): Promise<void> {
  const agentDir = await createAgentDir();
  try {
    await run(agentDir);
  } finally {
    await fs.rm(agentDir, { recursive: true, force: true });
  }
}

function writeAuthProfilesSqlite(agentDir: string, store: AuthProfileStore): void {
  writePersistedAuthProfileStoreRaw(store, agentDir);
}

describe("discoverAuthStorage", () => {
  it("converts runtime auth profiles into agent discovery credentials", () => {
    const credentials = resolveAgentCredentialMapFromStore({
      version: 1,
      profiles: {
        "openrouter:default": {
          type: "api_key",
          provider: "openrouter",
          key: "sk-or-v1-runtime",
        },
        "anthropic:default": {
          type: "token",
          provider: "anthropic",
          token: "sk-ant-runtime",
        },
        "openai:default": {
          type: "oauth",
          provider: "openai",
          access: "oauth-access",
          refresh: "oauth-refresh",
          expires: Date.now() + 60_000,
        },
      },
    });

    expect(credentials.openrouter).toEqual({
      type: "api_key",
      key: "sk-or-v1-runtime",
    });
    expect(credentials.anthropic).toEqual({
      type: "api_key",
      key: "sk-ant-runtime",
    });
    const codexCredential = credentials["openai"] as
      | { type?: string; access?: string; refresh?: string }
      | undefined;
    expect(codexCredential?.type).toBe("oauth");
    expect(codexCredential?.access).toBe("oauth-access");
    expect(codexCredential?.refresh).toBe("oauth-refresh");
  });

  it("drops runtime auth profiles with out-of-range expiry values", () => {
    const credentials = resolveAgentCredentialMapFromStore({
      version: 1,
      profiles: {
        "anthropic:bad-token-expiry": {
          type: "token",
          provider: "anthropic",
          token: "sk-ant-runtime",
          expires: MAX_DATE_TIMESTAMP_MS + 1,
        },
        "openai:bad-oauth-expiry": {
          type: "oauth",
          provider: "openai",
          access: "oauth-access",
          refresh: "oauth-refresh",
          expires: MAX_DATE_TIMESTAMP_MS + 1,
        },
      },
    });

    expect(credentials.anthropic).toBeUndefined();
    expect(credentials.openai).toBeUndefined();
  });

  it("keeps keyRef and tokenRef profiles visible only for read-only agent discovery", () => {
    const credentials = resolveAgentCredentialMapFromStore({
      version: 1,
      profiles: {
        "openrouter:default": {
          type: "api_key",
          provider: "openrouter",
          keyRef: { source: "exec", provider: "keychain", id: "OPENROUTER_API_KEY" },
        },
        "anthropic:default": {
          type: "token",
          provider: "anthropic",
          tokenRef: { source: "env", provider: "default", id: "ANTHROPIC_AUTH_TOKEN" },
        },
        "expired:default": {
          type: "token",
          provider: "expired",
          tokenRef: { source: "env", provider: "default", id: "EXPIRED_AUTH_TOKEN" },
          expires: Date.now() - 1_000,
        },
      },
    });
    const discoveryCredentials = resolveAgentCredentialMapFromStore(
      {
        version: 1,
        profiles: {
          "openrouter:default": {
            type: "api_key",
            provider: "openrouter",
            keyRef: { source: "exec", provider: "keychain", id: "OPENROUTER_API_KEY" },
          },
          "anthropic:default": {
            type: "token",
            provider: "anthropic",
            tokenRef: { source: "env", provider: "default", id: "ANTHROPIC_AUTH_TOKEN" },
          },
          "expired:default": {
            type: "token",
            provider: "expired",
            tokenRef: { source: "env", provider: "default", id: "EXPIRED_AUTH_TOKEN" },
            expires: Date.now() - 1_000,
          },
        },
      },
      { includeSecretRefPlaceholders: true },
    );

    expect(credentials.openrouter).toBeUndefined();
    expect(credentials.anthropic).toBeUndefined();
    expect(discoveryCredentials.openrouter?.type).toBe("api_key");
    expect(discoveryCredentials.anthropic?.type).toBe("api_key");
    expect(discoveryCredentials.expired).toBeUndefined();
  });

  it("marks keyRef-only auth profiles configured for read-only model discovery", async () => {
    await withAgentDir(async (agentDir) => {
      writeAuthProfilesSqlite(agentDir, {
        version: 1,
        profiles: {
          "fixture-ref-provider:default": {
            type: "api_key",
            provider: "fixture-ref-provider",
            keyRef: { source: "exec", provider: "keychain", id: "FIXTURE_API_KEY" },
          },
        },
      });

      const readOnlyStorage = discoverAuthStorage(agentDir, {
        readOnly: true,
        skipExternalAuthProfiles: true,
        env: {},
      });
      const runtimeStorage = discoverAuthStorage(agentDir, {
        skipExternalAuthProfiles: true,
        env: {},
      });

      expect(readOnlyStorage.hasAuth("fixture-ref-provider")).toBe(true);
      expect(runtimeStorage.hasAuth("fixture-ref-provider")).toBe(false);
    });
  });

  it("includes env-backed provider auth when no auth profile exists", () => {
    const previousMistral = process.env.MISTRAL_API_KEY;
    const previousBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
    const previousDisableBundledPlugins = process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS;
    process.env.MISTRAL_API_KEY = "mistral-env-test-key";
    delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
    delete process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS;
    try {
      const credentials = addEnvBackedAgentCredentials({}, { env: process.env });

      expect(credentials.mistral).toEqual({
        type: "api_key",
        key: "mistral-env-test-key",
      });
    } finally {
      if (previousMistral === undefined) {
        delete process.env.MISTRAL_API_KEY;
      } else {
        process.env.MISTRAL_API_KEY = previousMistral;
      }
      if (previousBundledPluginsDir === undefined) {
        delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
      } else {
        process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = previousBundledPluginsDir;
      }
      if (previousDisableBundledPlugins === undefined) {
        delete process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS;
      } else {
        process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS = previousDisableBundledPlugins;
      }
    }
  });

  it("includes workspace-scoped auth evidence in agent discovery credentials", () => {
    const credentials = addEnvBackedAgentCredentials(
      {},
      {
        env: {},
        workspaceDir: "/tmp/workspace",
      },
    );

    expect(credentials["workspace-cloud"]).toEqual({
      type: "api_key",
      key: "workspace-cloud-local-credentials",
    });
  });
});
