import { describe, expect, it } from "vitest";
import {
  computeAvailableHours,
  computeBookedHours,
  computeChargeability,
} from "../allocation/chargeability.js";

// Standard Mon–Fri 8h availability
const stdAvail = { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 };

// Week of 2025-01-06 (Mon) to 2025-01-10 (Fri)
const weekStart = new Date("2025-01-06");
const weekEnd   = new Date("2025-01-10");

describe("computeAvailableHours", () => {
  it("Mon–Fri 8h resource: 1 calendar week = 40 available hours", () => {
    expect(computeAvailableHours(stdAvail, weekStart, weekEnd)).toBe(40);
  });

  it("excludes Saturday and Sunday when not in availability", () => {
    // 2025-01-04 (Sat) to 2025-01-05 (Sun)
    expect(computeAvailableHours(stdAvail, new Date("2025-01-04"), new Date("2025-01-05"))).toBe(0);
  });

  it("counts Saturday when enabled in availability", () => {
    const avail = { ...stdAvail, saturday: 4 };
    // Mon 06 to Sat 11 Jan: 5 weekdays × 8 + 1 Saturday × 4 = 44
    expect(computeAvailableHours(avail, new Date("2025-01-06"), new Date("2025-01-11"))).toBe(44);
  });

  it("single day period (Monday): returns daily hours", () => {
    expect(computeAvailableHours(stdAvail, weekStart, weekStart)).toBe(8);
  });
});

describe("computeBookedHours", () => {
  it("no allocations → bookedHours = 0", () => {
    expect(computeBookedHours(stdAvail, [], weekStart, weekEnd)).toBe(0);
  });

  it("full 8h allocation covering entire period counts all working days", () => {
    const alloc = { startDate: weekStart, endDate: weekEnd, hoursPerDay: 8 };
    expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(40);
  });

  it("allocation partially overlapping: only overlap days counted", () => {
    // Allocation spans Wed-Sun; period is Mon-Fri → overlap is Wed-Fri (3 days)
    const alloc = {
      startDate: new Date("2025-01-08"), // Wed
      endDate:   new Date("2025-01-12"), // Sun
      hoursPerDay: 8,
    };
    expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(24); // 3 days × 8
  });

  it("allocation spanning weekends: weekends excluded from booked hours", () => {
    // Mon 06 to Mon 13 (spans a weekend), but weekend has 0 avail
    const alloc = {
      startDate: new Date("2025-01-06"),
      endDate:   new Date("2025-01-13"),
      hoursPerDay: 8,
    };
    // Period is Mon-Fri week only
    expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(40);
  });

  it("Saturday with availability counted when allocation covers it", () => {
    const avail = { ...stdAvail, saturday: 4 };
    const alloc = {
      startDate: new Date("2025-01-06"), // Mon
      endDate:   new Date("2025-01-11"), // Sat
      hoursPerDay: 6,
    };
    // Mon–Fri: 5 days × 6 = 30, Sat: 1 day × 6 = 6 → total 36
    expect(computeBookedHours(avail, [alloc], new Date("2025-01-06"), new Date("2025-01-11"))).toBe(36);
  });

  it("allocation entirely before period: no hours counted", () => {
    const alloc = {
      startDate: new Date("2024-12-30"),
      endDate:   new Date("2025-01-03"),
      hoursPerDay: 8,
    };
    expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(0);
  });

  it("allocation entirely after period: no hours counted", () => {
    const alloc = {
      startDate: new Date("2025-01-13"),
      endDate:   new Date("2025-01-17"),
      hoursPerDay: 8,
    };
    expect(computeBookedHours(stdAvail, [alloc], weekStart, weekEnd)).toBe(0);
  });
});

describe("computeChargeability", () => {
  it("no allocations → chargeability = 0, bookedHours = 0", () => {
    const result = computeChargeability(stdAvail, [], weekStart, weekEnd);
    expect(result.chargeability).toBe(0);
    expect(result.bookedHours).toBe(0);
    expect(result.availableHours).toBe(40);
  });

  it("full 8h allocation covering entire period → chargeability = 100", () => {
    const alloc = { startDate: weekStart, endDate: weekEnd, hoursPerDay: 8 };
    const result = computeChargeability(stdAvail, [alloc], weekStart, weekEnd);
    expect(result.chargeability).toBe(100);
    expect(result.bookedHours).toBe(40);
  });

  it("50% allocation → chargeability = 50", () => {
    const alloc = { startDate: weekStart, endDate: weekEnd, hoursPerDay: 4 };
    const result = computeChargeability(stdAvail, [alloc], weekStart, weekEnd);
    expect(result.chargeability).toBe(50);
    expect(result.bookedHours).toBe(20);
  });

  it("over-allocated (booked > available): chargeability capped at 100", () => {
    // Two allocations totalling 12h/day on a resource with 8h availability
    const allocs = [
      { startDate: weekStart, endDate: weekEnd, hoursPerDay: 8 },
      { startDate: weekStart, endDate: weekEnd, hoursPerDay: 4 },
    ];
    const result = computeChargeability(stdAvail, allocs, weekStart, weekEnd);
    expect(result.chargeability).toBe(100);
    expect(result.bookedHours).toBe(60); // not capped in raw value
  });

  it("single day period (start === end): correct result", () => {
    const alloc = { startDate: weekStart, endDate: weekStart, hoursPerDay: 4 };
    const result = computeChargeability(stdAvail, [alloc], weekStart, weekStart);
    expect(result.availableHours).toBe(8);
    expect(result.bookedHours).toBe(4);
    expect(result.chargeability).toBe(50);
  });

  it("zero available hours (all weekend period): chargeability = 0", () => {
    const alloc = {
      startDate: new Date("2025-01-04"),
      endDate:   new Date("2025-01-05"),
      hoursPerDay: 8,
    };
    const result = computeChargeability(stdAvail, [alloc], new Date("2025-01-04"), new Date("2025-01-05"));
    expect(result.availableHours).toBe(0);
    expect(result.chargeability).toBe(0);
  });
});
