import { AllocationStatus } from "@capakraken/shared";
import { describe, expect, it } from "vitest";
import { analyzeUtilization, findCapacityWindows } from "../capacity-analyzer.js";
import type { CapacityAnalysisInput } from "../capacity-analyzer.js";

const standardAvailability = {
  sunday: 0,
  monday: 8,
  tuesday: 8,
  wednesday: 8,
  thursday: 8,
  friday: 8,
  saturday: 0,
};

const resource = {
  id: "res-1",
  displayName: "Jane Doe",
  chargeabilityTarget: 80,
  availability: standardAvailability,
};

describe("analyzeUtilization", () => {
  it("computes chargeability for a single chargeable allocation", () => {
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: new Date("2026-03-09"), // Monday
          endDate: new Date("2026-03-13"), // Friday
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
          projectName: "Project A",
          isChargeable: true,
        },
      ],
      analysisStart: new Date("2026-03-09"),
      analysisEnd: new Date("2026-03-13"),
    };

    const result = analyzeUtilization(input);
    expect(result.resourceId).toBe("res-1");
    expect(result.currentChargeability).toBe(100);
    expect(result.chargeabilityGap).toBe(-20); // target 80, actual 100
    expect(result.overallocatedDays).toHaveLength(0);
    expect(result.underutilizedDays).toHaveLength(0);
  });

  it("detects overallocated days", () => {
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-09"),
          hoursPerDay: 5,
          status: AllocationStatus.CONFIRMED,
          projectName: "A",
          isChargeable: true,
        },
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-09"),
          hoursPerDay: 5,
          status: AllocationStatus.CONFIRMED,
          projectName: "B",
          isChargeable: true,
        },
      ],
      analysisStart: new Date("2026-03-09"),
      analysisEnd: new Date("2026-03-09"),
    };

    const result = analyzeUtilization(input);
    expect(result.overallocatedDays.length).toBeGreaterThanOrEqual(1);
  });

  it("detects underutilized days (below 50%)", () => {
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-09"),
          hoursPerDay: 2,
          status: AllocationStatus.CONFIRMED,
          projectName: "A",
          isChargeable: true,
        },
      ],
      analysisStart: new Date("2026-03-09"),
      analysisEnd: new Date("2026-03-09"),
    };

    const result = analyzeUtilization(input);
    expect(result.underutilizedDays).toHaveLength(1);
  });

  it("ignores allocations with inactive status", () => {
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-13"),
          hoursPerDay: 8,
          status: AllocationStatus.CANCELLED,
          projectName: "Cancelled",
          isChargeable: true,
        },
      ],
      analysisStart: new Date("2026-03-09"),
      analysisEnd: new Date("2026-03-13"),
    };

    const result = analyzeUtilization(input);
    expect(result.currentChargeability).toBe(0);
    expect(result.allocations).toHaveLength(0);
  });

  it("separates chargeable from non-chargeable hours", () => {
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-09"),
          hoursPerDay: 4,
          status: AllocationStatus.CONFIRMED,
          projectName: "Chargeable",
          isChargeable: true,
        },
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-09"),
          hoursPerDay: 4,
          status: AllocationStatus.CONFIRMED,
          projectName: "Internal",
          isChargeable: false,
        },
      ],
      analysisStart: new Date("2026-03-09"),
      analysisEnd: new Date("2026-03-09"),
    };

    const result = analyzeUtilization(input);
    expect(result.currentChargeability).toBe(50);
    expect(result.overallocatedDays).toHaveLength(0);
  });

  it("skips weekends", () => {
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [],
      analysisStart: new Date("2026-03-07"), // Saturday
      analysisEnd: new Date("2026-03-08"), // Sunday
    };

    const result = analyzeUtilization(input);
    expect(result.currentChargeability).toBe(0);
    expect(result.overallocatedDays).toHaveLength(0);
    expect(result.underutilizedDays).toHaveLength(0);
  });
});

describe("findCapacityWindows", () => {
  const simpleResource = {
    id: "res-1",
    displayName: "Jane Doe",
    availability: standardAvailability,
  };

  it("finds a full-week window with no allocations", () => {
    const windows = findCapacityWindows(
      simpleResource,
      [],
      new Date("2026-03-09"), // Monday
      new Date("2026-03-13"), // Friday
    );
    expect(windows).toHaveLength(1);
    expect(windows[0]!.availableDays).toBe(5);
    expect(windows[0]!.availableHoursPerDay).toBe(8);
    expect(windows[0]!.totalAvailableHours).toBe(40);
  });

  it("reduces available hours when partially allocated", () => {
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-13"),
          hoursPerDay: 4,
          status: AllocationStatus.CONFIRMED,
        },
      ],
      new Date("2026-03-09"),
      new Date("2026-03-13"),
    );
    expect(windows).toHaveLength(1);
    expect(windows[0]!.availableHoursPerDay).toBe(4);
    expect(windows[0]!.totalAvailableHours).toBe(20);
  });

  it("returns no windows when fully allocated", () => {
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-13"),
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
        },
      ],
      new Date("2026-03-09"),
      new Date("2026-03-13"),
    );
    expect(windows).toHaveLength(0);
  });

  it("ignores cancelled allocations", () => {
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-13"),
          hoursPerDay: 8,
          status: AllocationStatus.CANCELLED,
        },
      ],
      new Date("2026-03-09"),
      new Date("2026-03-13"),
    );
    expect(windows).toHaveLength(1);
    expect(windows[0]!.availableDays).toBe(5);
  });

  it("splits windows around gaps", () => {
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: new Date("2026-03-11"), // Wed only fully booked
          endDate: new Date("2026-03-11"),
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
        },
      ],
      new Date("2026-03-09"), // Mon
      new Date("2026-03-13"), // Fri
    );
    // Two windows: Mon-Tue, Thu-Fri
    expect(windows).toHaveLength(2);
    expect(windows[0]!.availableDays).toBe(2);
    expect(windows[1]!.availableDays).toBe(2);
  });

  it("respects minAvailableHoursPerDay threshold", () => {
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: new Date("2026-03-09"),
          endDate: new Date("2026-03-09"),
          hoursPerDay: 6,
          status: AllocationStatus.CONFIRMED,
        },
      ],
      new Date("2026-03-09"),
      new Date("2026-03-09"),
      4, // need at least 4 hours free
    );
    // Only 2 hours free (8-6), threshold is 4 → no window
    expect(windows).toHaveLength(0);
  });
});
