import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import {
  applyKilocodeProviderConfig,
  applyKilocodeConfig,
  KILOCODE_BASE_URL,
  KILOCODE_DEFAULT_MODEL_REF,
} from "../../extensions/kilocode/onboard.js";
import { resolveApiKeyForProvider, resolveEnvApiKey } from "../agents/model-auth.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
import {
  buildKilocodeModelDefinition,
  KILOCODE_DEFAULT_MODEL_ID,
  KILOCODE_DEFAULT_CONTEXT_WINDOW,
  KILOCODE_DEFAULT_MAX_TOKENS,
  KILOCODE_DEFAULT_COST,
} from "../plugin-sdk/provider-models.js";
import { captureEnv } from "../test-utils/env.js";

const emptyCfg: OpenClawConfig = {};
const KILOCODE_MODEL_IDS = ["kilo/auto"];

describe("Kilo Gateway provider config", () => {
  describe("constants", () => {
    it("KILOCODE_BASE_URL points to kilo openrouter endpoint", () => {
      expect(KILOCODE_BASE_URL).toBe("https://api.kilo.ai/api/gateway/");
    });

    it("KILOCODE_DEFAULT_MODEL_REF includes provider prefix", () => {
      expect(KILOCODE_DEFAULT_MODEL_REF).toBe("kilocode/kilo/auto");
    });

    it("KILOCODE_DEFAULT_MODEL_ID is kilo/auto", () => {
      expect(KILOCODE_DEFAULT_MODEL_ID).toBe("kilo/auto");
    });
  });

  describe("buildKilocodeModelDefinition", () => {
    it("returns correct model shape", () => {
      const model = buildKilocodeModelDefinition();
      expect(model.id).toBe(KILOCODE_DEFAULT_MODEL_ID);
      expect(model.name).toBe("Kilo Auto");
      expect(model.reasoning).toBe(true);
      expect(model.input).toEqual(["text", "image"]);
      expect(model.contextWindow).toBe(KILOCODE_DEFAULT_CONTEXT_WINDOW);
      expect(model.maxTokens).toBe(KILOCODE_DEFAULT_MAX_TOKENS);
      expect(model.cost).toEqual(KILOCODE_DEFAULT_COST);
    });
  });

  describe("applyKilocodeProviderConfig", () => {
    it("registers kilocode provider with correct baseUrl and api", () => {
      const result = applyKilocodeProviderConfig(emptyCfg);
      const provider = result.models?.providers?.kilocode;
      expect(provider).toBeDefined();
      expect(provider?.baseUrl).toBe(KILOCODE_BASE_URL);
      expect(provider?.api).toBe("openai-completions");
    });

    it("includes the default model in the provider model list", () => {
      const result = applyKilocodeProviderConfig(emptyCfg);
      const provider = result.models?.providers?.kilocode;
      const models = provider?.models;
      expect(Array.isArray(models)).toBe(true);
      const modelIds = models?.map((m) => m.id) ?? [];
      expect(modelIds).toContain(KILOCODE_DEFAULT_MODEL_ID);
    });

    it("surfaces the full Kilo model catalog", () => {
      const result = applyKilocodeProviderConfig(emptyCfg);
      const provider = result.models?.providers?.kilocode;
      const modelIds = provider?.models?.map((m) => m.id) ?? [];
      for (const modelId of KILOCODE_MODEL_IDS) {
        expect(modelIds).toContain(modelId);
      }
    });

    it("appends missing catalog models to existing Kilo provider config", () => {
      const result = applyKilocodeProviderConfig({
        models: {
          providers: {
            kilocode: {
              baseUrl: KILOCODE_BASE_URL,
              api: "openai-completions",
              models: [buildKilocodeModelDefinition()],
            },
          },
        },
      });
      const modelIds = result.models?.providers?.kilocode?.models?.map((m) => m.id) ?? [];
      for (const modelId of KILOCODE_MODEL_IDS) {
        expect(modelIds).toContain(modelId);
      }
    });

    it("sets Kilo Gateway alias in agent default models", () => {
      const result = applyKilocodeProviderConfig(emptyCfg);
      const agentModel = result.agents?.defaults?.models?.[KILOCODE_DEFAULT_MODEL_REF];
      expect(agentModel).toBeDefined();
      expect(agentModel?.alias).toBe("Kilo Gateway");
    });

    it("preserves existing alias if already set", () => {
      const cfg: OpenClawConfig = {
        agents: {
          defaults: {
            models: {
              [KILOCODE_DEFAULT_MODEL_REF]: { alias: "My Custom Alias" },
            },
          },
        },
      };
      const result = applyKilocodeProviderConfig(cfg);
      const agentModel = result.agents?.defaults?.models?.[KILOCODE_DEFAULT_MODEL_REF];
      expect(agentModel?.alias).toBe("My Custom Alias");
    });

    it("does not change the default model selection", () => {
      const cfg: OpenClawConfig = {
        agents: {
          defaults: {
            model: { primary: "openai/gpt-5" },
          },
        },
      };
      const result = applyKilocodeProviderConfig(cfg);
      expect(resolveAgentModelPrimaryValue(result.agents?.defaults?.model)).toBe("openai/gpt-5");
    });
  });

  describe("applyKilocodeConfig", () => {
    it("sets kilocode as the default model", () => {
      const result = applyKilocodeConfig(emptyCfg);
      expect(resolveAgentModelPrimaryValue(result.agents?.defaults?.model)).toBe(
        KILOCODE_DEFAULT_MODEL_REF,
      );
    });

    it("also registers the provider", () => {
      const result = applyKilocodeConfig(emptyCfg);
      const provider = result.models?.providers?.kilocode;
      expect(provider).toBeDefined();
      expect(provider?.baseUrl).toBe(KILOCODE_BASE_URL);
    });
  });

  describe("env var resolution", () => {
    it("resolves KILOCODE_API_KEY from env", () => {
      const envSnapshot = captureEnv(["KILOCODE_API_KEY"]);
      process.env.KILOCODE_API_KEY = "test-kilo-key"; // pragma: allowlist secret

      try {
        const result = resolveEnvApiKey("kilocode");
        expect(result).not.toBeNull();
        expect(result?.apiKey).toBe("test-kilo-key");
        expect(result?.source).toContain("KILOCODE_API_KEY");
      } finally {
        envSnapshot.restore();
      }
    });

    it("returns null when KILOCODE_API_KEY is not set", () => {
      const envSnapshot = captureEnv(["KILOCODE_API_KEY"]);
      delete process.env.KILOCODE_API_KEY;

      try {
        const result = resolveEnvApiKey("kilocode");
        expect(result).toBeNull();
      } finally {
        envSnapshot.restore();
      }
    });

    it("resolves the kilocode api key via resolveApiKeyForProvider", async () => {
      const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
      const envSnapshot = captureEnv(["KILOCODE_API_KEY"]);
      process.env.KILOCODE_API_KEY = "kilo-provider-test-key"; // pragma: allowlist secret

      try {
        const auth = await resolveApiKeyForProvider({
          provider: "kilocode",
          agentDir,
        });

        expect(auth.apiKey).toBe("kilo-provider-test-key");
        expect(auth.mode).toBe("api-key");
        expect(auth.source).toContain("KILOCODE_API_KEY");
      } finally {
        envSnapshot.restore();
      }
    });
  });
});
