/**
 * Vacation overlap edge-case tests for analyzeUtilization and findCapacityWindows.
 *
 * The capacity analyzer itself does NOT have direct vacation knowledge — vacations
 * are represented as allocations (with hoursPerDay equal to the resource's full
 * availability) from the caller's perspective.  These tests verify that the
 * analyzeUtilization and findCapacityWindows functions correctly handle the
 * resulting utilization when vacation blocks are modelled as full-day allocations.
 */
import { AllocationStatus } from "@nexus/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-vacation",
  displayName: "Vacation Tester",
  chargeabilityTarget: 80,
  availability: standardAvailability,
};

const simpleResource = {
  id: "res-vacation",
  displayName: "Vacation Tester",
  availability: standardAvailability,
};

// Monday 2026-03-09 … Friday 2026-03-13 (5 working days)
const MON = new Date("2026-03-09");
const FRI = new Date("2026-03-13");

describe("analyzeUtilization — vacation overlap", () => {
  it("resource with vacation covering the entire allocation period results in 0 free chargeable hours", () => {
    // Vacation modelled as a full-day non-chargeable allocation for the whole week
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: MON,
          endDate: FRI,
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
          projectName: "Vacation",
          isChargeable: false,
        },
      ],
      analysisStart: MON,
      analysisEnd: FRI,
    };

    const result = analyzeUtilization(input);

    // No chargeable hours, but all available hours are consumed by vacation
    expect(result.currentChargeability).toBe(0);
    // All 5 days are allocated at 8/8 — not overallocated, not underutilized
    expect(result.overallocatedDays).toHaveLength(0);
    // Each day: allocatedHours (8) === availableHours (8), which is NOT < 50%, so no underutilized
    expect(result.underutilizedDays).toHaveLength(0);
  });

  it("resource with partial vacation overlap has correctly reduced available chargeable hours", () => {
    // 2-day vacation Mon-Tue, then project allocation Wed-Fri
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: MON,
          endDate: new Date("2026-03-10"), // Mon–Tue
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
          projectName: "Vacation",
          isChargeable: false,
        },
        {
          startDate: new Date("2026-03-11"), // Wed–Fri
          endDate: FRI,
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
          projectName: "Project A",
          isChargeable: true,
        },
      ],
      analysisStart: MON,
      analysisEnd: FRI,
    };

    const result = analyzeUtilization(input);

    // 5 working days × 8 h = 40 h total available
    // Chargeable hours: 3 days × 8 h = 24 h → 24/40 = 60%
    expect(result.currentChargeability).toBeCloseTo(60, 5);
    expect(result.overallocatedDays).toHaveLength(0);
    expect(result.underutilizedDays).toHaveLength(0);
  });

  it("multiple overlapping vacation blocks are each counted separately without double-counting overallocation when summed hours exceed availability", () => {
    // Two full-day vacation records on the same Monday (simulating duplicate entries)
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: MON,
          endDate: MON,
          hoursPerDay: 5,
          status: AllocationStatus.CONFIRMED,
          projectName: "Vacation A",
          isChargeable: false,
        },
        {
          startDate: MON,
          endDate: MON,
          hoursPerDay: 5,
          status: AllocationStatus.CONFIRMED,
          projectName: "Vacation B",
          isChargeable: false,
        },
      ],
      analysisStart: MON,
      analysisEnd: MON,
    };

    const result = analyzeUtilization(input);

    // 10 h allocated on a day with 8 h availability → overallocated
    expect(result.overallocatedDays).toHaveLength(1);
    // Date string contains the correct date components (timezone-safe check)
    expect(result.overallocatedDays[0]).toMatch(/2026-03-0[89]/);
    expect(result.currentChargeability).toBe(0); // all non-chargeable
  });

  it("vacation on Saturday/Sunday does not reduce working hours (availability = 0 on weekends)", () => {
    // Vacation over a weekend — should have zero effect on computed utilization
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: new Date("2026-03-07"), // Saturday
          endDate: new Date("2026-03-08"), // Sunday
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
          projectName: "Weekend Vacation",
          isChargeable: false,
        },
      ],
      analysisStart: new Date("2026-03-07"),
      analysisEnd: new Date("2026-03-08"),
    };

    const result = analyzeUtilization(input);

    // Weekends skipped entirely — totalWorkingDays = 0, no days affected
    expect(result.overallocatedDays).toHaveLength(0);
    expect(result.underutilizedDays).toHaveLength(0);
    expect(result.currentChargeability).toBe(0);
    // allocations list still includes the weekend entry (it's just not active on working days)
    // but chargeability stays 0 because there are no working days in that window
  });

  it("vacation on weekend days within a full-week analysis window leaves weekday capacity intact", () => {
    // Analysis covers Mon–Sun. Vacation is Sat–Sun only.
    const input: CapacityAnalysisInput = {
      resource,
      allocations: [
        {
          startDate: new Date("2026-03-14"), // Saturday
          endDate: new Date("2026-03-15"), // Sunday
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
          projectName: "Weekend Vacation",
          isChargeable: false,
        },
      ],
      analysisStart: MON,
      analysisEnd: new Date("2026-03-15"), // Mon–Sun
    };

    const result = analyzeUtilization(input);

    // All 5 weekdays have zero allocation → all underutilized (0 < 50% of 8)
    expect(result.underutilizedDays).toHaveLength(5);
    expect(result.overallocatedDays).toHaveLength(0);
    expect(result.currentChargeability).toBe(0);
  });
});

describe("findCapacityWindows — vacation overlap", () => {
  it("vacation covering entire search period leaves no capacity windows (full-day blocking)", () => {
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: MON,
          endDate: FRI,
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
        },
      ],
      MON,
      FRI,
    );

    expect(windows).toHaveLength(0);
  });

  it("partial vacation overlap leaves capacity only on non-vacation days", () => {
    // Mon–Wed booked as vacation (8 h/day), Thu–Fri free
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: MON,
          endDate: new Date("2026-03-11"), // Mon–Wed
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
        },
      ],
      MON,
      FRI,
    );

    expect(windows).toHaveLength(1);
    expect(windows[0]!.availableDays).toBe(2); // Thu + Fri
    expect(windows[0]!.availableHoursPerDay).toBe(8);
    expect(windows[0]!.totalAvailableHours).toBe(16);
  });

  it("multiple non-overlapping vacation blocks create gaps in capacity", () => {
    // Mon booked, Tue free, Wed booked, Thu–Fri free
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: MON,
          endDate: MON,
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
        },
        {
          startDate: new Date("2026-03-11"), // Wed
          endDate: new Date("2026-03-11"),
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
        },
      ],
      MON,
      FRI,
    );

    // Tue alone, then Thu–Fri
    expect(windows).toHaveLength(2);
    expect(windows[0]!.availableDays).toBe(1); // Tue
    expect(windows[1]!.availableDays).toBe(2); // Thu–Fri
  });

  it("vacation on weekend days does not affect capacity windows for weekdays", () => {
    // Vacation on Sat–Sun should not close any weekday windows
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: new Date("2026-03-07"), // Saturday
          endDate: new Date("2026-03-08"), // Sunday
          hoursPerDay: 8,
          status: AllocationStatus.CONFIRMED,
        },
      ],
      MON,
      FRI,
    );

    // The weekend vacation falls outside the Mon–Fri window, so full week is free
    expect(windows).toHaveLength(1);
    expect(windows[0]!.availableDays).toBe(5);
    expect(windows[0]!.totalAvailableHours).toBe(40);
  });

  it("cancelled vacation has no effect on available capacity", () => {
    const windows = findCapacityWindows(
      simpleResource,
      [
        {
          startDate: MON,
          endDate: FRI,
          hoursPerDay: 8,
          status: AllocationStatus.CANCELLED,
        },
      ],
      MON,
      FRI,
    );

    expect(windows).toHaveLength(1);
    expect(windows[0]!.availableDays).toBe(5);
    expect(windows[0]!.totalAvailableHours).toBe(40);
  });
});
