import { describe, expect, it } from "vitest";
import {
  computeAvailabilityScore,
  computeCostScore,
  computeSkillScore,
  computeUtilizationScore,
  rankResources,
} from "../skill-matcher.js";

const mockSkills = [
  { skill: "TypeScript", proficiency: 5 as const, yearsExperience: 5 },
  { skill: "React", proficiency: 4 as const, yearsExperience: 3 },
  { skill: "Node.js", proficiency: 3 as const, yearsExperience: 2 },
];

describe("computeSkillScore", () => {
  it("returns 100 for no required skills", () => {
    const result = computeSkillScore(mockSkills, []);
    expect(result.score).toBe(100);
  });

  it("scores correctly for full match with max proficiency", () => {
    const result = computeSkillScore(mockSkills, ["TypeScript"]);
    // TypeScript proficiency 5/5 = 1.0, required portion = 70%, preferred = 30% (none)
    expect(result.score).toBeGreaterThan(90);
    expect(result.matchedRequired).toContain("TypeScript");
    expect(result.missing).toHaveLength(0);
  });

  it("scores lower for missing required skills", () => {
    const result = computeSkillScore(mockSkills, ["Python"]);
    expect(result.score).toBeLessThan(50);
    expect(result.missing).toContain("Python");
  });

  it("matches skills case-insensitively", () => {
    const result = computeSkillScore(mockSkills, ["typescript"]);
    expect(result.matchedRequired).toHaveLength(1);
  });

  it("accounts for preferred skills: matched preferred yields high score", () => {
    const withPref = computeSkillScore(mockSkills, ["TypeScript"], ["React"]);
    const withoutPref = computeSkillScore(mockSkills, ["TypeScript"], []);
    // Both should score high; preferred matching is reflected in breakdown
    expect(withPref.score).toBeGreaterThanOrEqual(80);
    expect(withoutPref.score).toBe(100);
    expect(withPref.matchedPreferred).toContain("React");
  });
});

describe("computeAvailabilityScore", () => {
  it("returns 100 for no conflicts", () => {
    expect(computeAvailabilityScore(false)).toBe(100);
  });

  it("reduces score by 10 per conflict day", () => {
    expect(computeAvailabilityScore(true, 5)).toBe(50);
  });

  it("floors at 0", () => {
    expect(computeAvailabilityScore(true, 15)).toBe(0);
  });
});

describe("computeCostScore", () => {
  it("returns 50 for no budget defined", () => {
    expect(computeCostScore(10000)).toBe(50);
  });

  it("returns 100 when resource cost is at budget", () => {
    expect(computeCostScore(10000, 10000)).toBe(100);
  });

  it("returns 100 when resource cost is below budget", () => {
    expect(computeCostScore(8000, 10000)).toBe(100);
  });

  it("penalizes resources above budget", () => {
    const score = computeCostScore(15000, 10000); // 1.5x budget
    expect(score).toBeLessThan(100);
    expect(score).toBeGreaterThanOrEqual(0);
  });
});

describe("computeUtilizationScore", () => {
  it("returns 100 for well below target (20+ gap)", () => {
    expect(computeUtilizationScore(50, 80)).toBe(100);
  });

  it("returns 0 for severely overallocated", () => {
    expect(computeUtilizationScore(110, 80)).toBe(0);
  });
});

describe("rankResources", () => {
  const resources = [
    {
      id: "r1",
      displayName: "Alice",
      eid: "EMP-001",
      skills: mockSkills,
      lcrCents: 8000,
      chargeabilityTarget: 80,
      currentUtilizationPercent: 50,
      hasAvailabilityConflicts: false,
    },
    {
      id: "r2",
      displayName: "Bob",
      eid: "EMP-002",
      skills: [{ skill: "Python", proficiency: 4 as const }],
      lcrCents: 12000,
      chargeabilityTarget: 80,
      currentUtilizationPercent: 90,
      hasAvailabilityConflicts: true,
      conflictDays: ["2025-01-06"],
    },
  ];

  it("returns suggestions sorted by score descending", () => {
    const results = rankResources({
      requiredSkills: ["TypeScript"],
      resources,
    });
    expect(results[0]?.score).toBeGreaterThanOrEqual(results[1]?.score ?? 0);
  });

  it("Alice ranks higher than Bob for TypeScript requirement", () => {
    const results = rankResources({
      requiredSkills: ["TypeScript"],
      resources,
    });
    expect(results[0]?.resourceId).toBe("r1");
  });

  it("returns all resources", () => {
    const results = rankResources({ requiredSkills: [], resources });
    expect(results).toHaveLength(2);
  });
});
