// src/server/services/modelSelection.test.ts
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as settingsRepo from "../repositories/settings";
import { pickProjectIdsForJob } from "./projectSelection";
import { scoreJobSuitability } from "./scorer";
import { generateTailoring } from "./summary";

// Mock the settings repository
vi.mock("../repositories/settings", () => ({
  getSetting: vi.fn(),
  getAllSettings: vi.fn(),
}));

describe("Model Selection Logic", () => {
  const originalEnv = process.env;

  beforeEach(() => {
    vi.resetAllMocks();
    // Set environment variables to ensure we don't hit early exits
    process.env = {
      ...originalEnv,
      OPENROUTER_API_KEY: "test-key",
      MODEL: "env-model",
    };

    // Mock getAllSettings to return empty settings (no overrides)
    vi.mocked(settingsRepo.getAllSettings).mockResolvedValue({});

    // Mock global fetch to capture the request and return a dummy success response
    global.fetch = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        choices: [
          {
            message: {
              content: JSON.stringify({
                score: 50,
                explanation: "ok",
                summary: "sum",
                headline: "head",
                skills: [],
                selectedProjectIds: ["1"],
              }),
            },
          },
        ],
      }),
    });
  });

  afterEach(() => {
    process.env = originalEnv;
    vi.restoreAllMocks();
  });

  describe("Scoring Service", () => {
    it("should use scoring specific model when set", async () => {
      vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => {
        if (key === "modelScorer") return "specific-scorer-model";
        if (key === "model") return "global-model";
        return null;
      });

      await scoreJobSuitability(
        { title: "Test Job", jobDescription: "desc" } as any,
        {},
      );

      const fetchCall = vi.mocked(fetch).mock.calls[0];
      const body = JSON.parse(fetchCall[1]?.body as string);
      expect(body.model).toBe("specific-scorer-model");
    });

    it("should fall back to global model for scoring when specific not set", async () => {
      vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => {
        if (key === "modelScorer") return null;
        if (key === "model") return "global-model";
        return null;
      });

      await scoreJobSuitability({ title: "Test Job" } as any, {});

      const fetchCall = vi.mocked(fetch).mock.calls[0];
      const body = JSON.parse(fetchCall[1]?.body as string);
      expect(body.model).toBe("global-model");
    });

    it("should fall back to env model for scoring when no settings set", async () => {
      vi.mocked(settingsRepo.getSetting).mockResolvedValue(null);

      await scoreJobSuitability({ title: "Test Job" } as any, {});

      const fetchCall = vi.mocked(fetch).mock.calls[0];
      const body = JSON.parse(fetchCall[1]?.body as string);
      expect(body.model).toBe("env-model");
    });
  });

  describe("Tailoring Service", () => {
    it("should use tailoring specific model when set", async () => {
      vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => {
        if (key === "modelTailoring") return "specific-tailoring-model";
        if (key === "model") return "global-model";
        return null;
      });

      await generateTailoring("job desc", {});

      const fetchCall = vi.mocked(fetch).mock.calls[0];
      const body = JSON.parse(fetchCall[1]?.body as string);
      expect(body.model).toBe("specific-tailoring-model");
    });

    it("should fall back to global model when specific not set", async () => {
      vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => {
        if (key === "modelTailoring") return null;
        if (key === "model") return "global-model";
        return null;
      });

      await generateTailoring("job desc", {});

      const fetchCall = vi.mocked(fetch).mock.calls[0];
      const body = JSON.parse(fetchCall[1]?.body as string);
      expect(body.model).toBe("global-model");
    });
  });

  describe("Project Selection Service", () => {
    it("should use project selection specific model when set", async () => {
      vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => {
        if (key === "modelProjectSelection") return "specific-project-model";
        if (key === "model") return "global-model";
        return null;
      });

      await pickProjectIdsForJob({
        jobDescription: "desc",
        eligibleProjects: [
          {
            id: "1",
            name: "p1",
            description: "d1",
            summaryText: "summary",
          } as any,
        ],
        desiredCount: 1,
      });

      const fetchCall = vi.mocked(fetch).mock.calls[0];
      const body = JSON.parse(fetchCall[1]?.body as string);
      expect(body.model).toBe("specific-project-model");
    });

    it("should fall back to global model when specific not set", async () => {
      vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => {
        if (key === "modelProjectSelection") return null;
        if (key === "model") return "global-model";
        return null;
      });

      await pickProjectIdsForJob({
        jobDescription: "desc",
        eligibleProjects: [
          {
            id: "1",
            name: "p1",
            description: "d1",
            summaryText: "summary",
          } as any,
        ],
        desiredCount: 1,
      });

      const fetchCall = vi.mocked(fetch).mock.calls[0];
      const body = JSON.parse(fetchCall[1]?.body as string);
      expect(body.model).toBe("global-model");
    });
  });
});
