import { beforeEach, describe, expect, it, vi } from "vitest";
import { AgentConfigService } from "./agent-config.service";
import { PrismaService } from "../prisma/prisma.service";
import { CryptoService } from "../crypto/crypto.service";

describe("AgentConfigService", () => {
  let service: AgentConfigService;

  const mockPrismaService = {
    userAgentConfig: {
      findUnique: vi.fn(),
    },
    llmProvider: {
      findMany: vi.fn(),
    },
    userContainer: {
      findUnique: vi.fn(),
      findMany: vi.fn(),
    },
    systemContainer: {
      findUnique: vi.fn(),
      findMany: vi.fn(),
    },
  };

  const mockCryptoService = {
    isEncrypted: vi.fn((value: string) => value.startsWith("enc:")),
    decrypt: vi.fn((value: string) => value.replace(/^enc:/, "")),
  };

  beforeEach(() => {
    vi.clearAllMocks();

    service = new AgentConfigService(
      mockPrismaService as unknown as PrismaService,
      mockCryptoService as unknown as CryptoService
    );
  });

  it("generateUserConfig returns valid openclaw.json structure", async () => {
    mockPrismaService.userAgentConfig.findUnique.mockResolvedValue({
      id: "cfg-1",
      userId: "user-1",
      primaryModel: "my-zai/glm-5",
    });

    mockPrismaService.userContainer.findUnique.mockResolvedValue({
      id: "container-1",
      userId: "user-1",
      gatewayPort: 19001,
    });

    mockPrismaService.llmProvider.findMany.mockResolvedValue([
      {
        id: "provider-1",
        userId: "user-1",
        name: "my-zai",
        displayName: "Z.ai",
        type: "zai",
        baseUrl: "https://api.z.ai/v1",
        apiKey: "enc:secret-zai-key",
        apiType: "openai-completions",
        models: [{ id: "glm-5" }],
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ]);

    const result = await service.generateUserConfig("user-1");

    expect(result).toEqual({
      gateway: {
        mode: "local",
        port: 19001,
        bind: "lan",
        auth: { mode: "token" },
        http: {
          endpoints: {
            chatCompletions: { enabled: true },
          },
        },
      },
      agents: {
        defaults: {
          model: {
            primary: "my-zai/glm-5",
          },
        },
      },
      models: {
        providers: {
          "my-zai": {
            apiKey: "secret-zai-key",
            baseUrl: "https://api.z.ai/v1",
            models: {
              "glm-5": {},
            },
          },
        },
      },
    });
  });

  it("generateUserConfig decrypts API keys correctly", async () => {
    mockPrismaService.userAgentConfig.findUnique.mockResolvedValue({
      id: "cfg-1",
      userId: "user-1",
      primaryModel: "openai-work/gpt-4.1",
    });

    mockPrismaService.userContainer.findUnique.mockResolvedValue({
      id: "container-1",
      userId: "user-1",
      gatewayPort: 18789,
    });

    mockPrismaService.llmProvider.findMany.mockResolvedValue([
      {
        id: "provider-1",
        userId: "user-1",
        name: "openai-work",
        displayName: "OpenAI Work",
        type: "openai",
        baseUrl: "https://api.openai.com/v1",
        apiKey: "enc:encrypted-openai-key",
        apiType: "openai-completions",
        models: [{ id: "gpt-4.1" }],
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ]);

    const result = await service.generateUserConfig("user-1");

    expect(mockCryptoService.decrypt).toHaveBeenCalledWith("enc:encrypted-openai-key");
    expect(result.models.providers["openai-work"]?.apiKey).toBe("encrypted-openai-key");
  });

  it("generateUserConfig handles user with no providers", async () => {
    mockPrismaService.userAgentConfig.findUnique.mockResolvedValue({
      id: "cfg-1",
      userId: "user-2",
      primaryModel: "openai/gpt-4o-mini",
    });

    mockPrismaService.userContainer.findUnique.mockResolvedValue({
      id: "container-2",
      userId: "user-2",
      gatewayPort: null,
    });

    mockPrismaService.llmProvider.findMany.mockResolvedValue([]);

    const result = await service.generateUserConfig("user-2");

    expect(result.models.providers).toEqual({});
    expect(result.gateway.port).toBe(18789);
  });

  it("validateContainerToken returns correct type for user container", async () => {
    mockPrismaService.userContainer.findMany.mockResolvedValue([
      {
        id: "user-container-1",
        gatewayToken: "enc:user-token-1",
      },
    ]);
    mockPrismaService.systemContainer.findMany.mockResolvedValue([]);

    const result = await service.validateContainerToken("user-token-1");

    expect(result).toEqual({
      type: "user",
      id: "user-container-1",
    });
  });

  it("validateContainerToken returns correct type for system container", async () => {
    mockPrismaService.userContainer.findMany.mockResolvedValue([]);
    mockPrismaService.systemContainer.findMany.mockResolvedValue([
      {
        id: "system-container-1",
        gatewayToken: "enc:system-token-1",
      },
    ]);

    const result = await service.validateContainerToken("system-token-1");

    expect(result).toEqual({
      type: "system",
      id: "system-container-1",
    });
  });

  it("validateContainerToken returns null for invalid token", async () => {
    mockPrismaService.userContainer.findMany.mockResolvedValue([
      {
        id: "user-container-1",
        gatewayToken: "enc:user-token-1",
      },
    ]);

    mockPrismaService.systemContainer.findMany.mockResolvedValue([
      {
        id: "system-container-1",
        gatewayToken: "enc:system-token-1",
      },
    ]);

    const result = await service.validateContainerToken("no-match");

    expect(result).toBeNull();
  });
});
