import { describe, expect, it, vi } from "vitest";
import {
  getDashboardBudgetForecast,
  getDashboardChargeabilityOverview,
  getDashboardDemand,
  getDashboardOverview,
  getDashboardPeakTimes,
  getDashboardProjectHealth,
  getDashboardTopValueResources,
} from "../index.js";

describe("dashboard use-cases", () => {
  it("computes overview budget summary from project budgets and allocation cost", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "assignment_1",
            projectId: "proj_1",
            resourceId: "res_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-03T00:00:00.000Z"),
            hoursPerDay: 8,
            dailyCostCents: 1_000,
            status: "ACTIVE",
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              status: "ACTIVE",
              orderType: "FIXED",
              dynamicFields: null,
            },
            resource: {
              id: "res_1",
              displayName: "Alice",
              chapter: "CGI",
            },
          },
        ]),
      },
      resource: {
        count: vi
          .fn()
          .mockResolvedValueOnce(4)
          .mockResolvedValueOnce(3),
        findMany: vi.fn().mockResolvedValue([
          { chapter: "CGI", chargeabilityTarget: 80 },
          { chapter: "CGI", chargeabilityTarget: 60 },
          { chapter: null, chargeabilityTarget: null },
        ]),
      },
      project: {
        count: vi.fn().mockResolvedValue(2),
        findMany: vi.fn().mockResolvedValue([
          { status: "ACTIVE", budgetCents: 100_000 },
          { status: "DRAFT", budgetCents: 50_000 },
        ]),
      },
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      auditLog: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "audit_1",
            entityType: "Allocation",
            action: "CREATE",
            createdAt: new Date("2026-03-05T10:00:00.000Z"),
          },
        ]),
      },
      vacation: {
        count: vi.fn().mockResolvedValue(2),
      },
      estimate: {
        count: vi.fn().mockResolvedValue(5),
      },
    };

    const result = await getDashboardOverview(db as never);

    expect(result.budgetSummary).toEqual({
      totalBudgetCents: 150_000,
      totalCostCents: 3_000,
      avgUtilizationPercent: 2,
    });
    expect(result.projectsByStatus).toEqual([
      { status: "ACTIVE", count: 1 },
      { status: "DRAFT", count: 1 },
    ]);
    expect(result.approvedVacations).toBe(2);
    expect(result.totalEstimates).toBe(5);
    expect(result.chapterUtilization).toEqual([
      { chapter: "CGI", resourceCount: 2, avgChargeabilityTarget: 70 },
      { chapter: "Unassigned", resourceCount: 1, avgChargeabilityTarget: 0 },
    ]);
  });

  it("avoids double-counting linked legacy allocations in overview budget totals", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "assignment_1",
            projectId: "proj_1",
            resourceId: "res_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 8,
            dailyCostCents: 2_000,
            status: "ACTIVE",
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              status: "ACTIVE",
              orderType: "FIXED",
            },
            resource: {
              id: "res_1",
              displayName: "Alice",
              chapter: "CGI",
            },
          },
        ]),
      },
      resource: {
        count: vi
          .fn()
          .mockResolvedValueOnce(1)
          .mockResolvedValueOnce(1),
        findMany: vi.fn().mockResolvedValue([{ chapter: "CGI", chargeabilityTarget: 80 }]),
      },
      project: {
        count: vi.fn().mockResolvedValue(1),
        findMany: vi.fn().mockResolvedValue([{ status: "ACTIVE", budgetCents: 100_000 }]),
      },
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      auditLog: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      vacation: {
        count: vi.fn().mockResolvedValue(0),
      },
      estimate: {
        count: vi.fn().mockResolvedValue(0),
      },
    };

    const result = await getDashboardOverview(db as never);

    expect(result.budgetSummary).toEqual({
      totalBudgetCents: 100_000,
      totalCostCents: 4_000,
      avgUtilizationPercent: 4,
    });
  });

  it("counts explicit demand and assignment rows in overview totals even without legacy allocation rows", async () => {
    const db = {
      assignment: {
        findMany: vi
          .fn()
          .mockResolvedValueOnce([
            {
              id: "assignment_explicit",
              demandRequirementId: "demand_explicit",
              projectId: "proj_1",
              resourceId: "res_1",
              startDate: new Date("2026-03-01T00:00:00.000Z"),
              endDate: new Date("2026-03-02T00:00:00.000Z"),
              hoursPerDay: 8,
              percentage: 100,
              role: "Compositor",
              roleId: "role_comp",
              dailyCostCents: 2_000,
              status: "ACTIVE",
              metadata: {},
              createdAt: new Date("2026-03-01T00:00:00.000Z"),
              updatedAt: new Date("2026-03-01T00:00:00.000Z"),
            },
          ])
          .mockResolvedValueOnce([
            {
              id: "assignment_explicit",
              projectId: "proj_1",
              resourceId: "res_1",
              startDate: new Date("2026-03-01T00:00:00.000Z"),
              endDate: new Date("2026-03-02T00:00:00.000Z"),
              hoursPerDay: 8,
              dailyCostCents: 2_000,
              status: "ACTIVE",
              project: {
                id: "proj_1",
                name: "Alpha",
                shortCode: "ALPHA",
                status: "ACTIVE",
                orderType: "FIXED",
              },
              resource: {
                id: "res_1",
                displayName: "Alice",
                chapter: "CGI",
              },
            },
          ]),
      },
      resource: {
        count: vi
          .fn()
          .mockResolvedValueOnce(1)
          .mockResolvedValueOnce(1),
        findMany: vi.fn().mockResolvedValue([{ chapter: "CGI", chargeabilityTarget: 80 }]),
      },
      project: {
        count: vi.fn().mockResolvedValue(1),
        findMany: vi.fn().mockResolvedValue([{ status: "ACTIVE", budgetCents: 100_000 }]),
      },
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "demand_explicit",
            projectId: "proj_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 8,
            percentage: 100,
            role: "Compositor",
            roleId: "role_comp",
            headcount: 1,
            status: "ACTIVE",
            metadata: {},
            createdAt: new Date("2026-03-01T00:00:00.000Z"),
            updatedAt: new Date("2026-03-01T00:00:00.000Z"),
          },
          {
            id: "demand_cancelled",
            projectId: "proj_2",
            startDate: new Date("2026-03-03T00:00:00.000Z"),
            endDate: new Date("2026-03-03T00:00:00.000Z"),
            hoursPerDay: 4,
            percentage: 50,
            role: "FX",
            roleId: "role_fx",
            headcount: 1,
            status: "CANCELLED",
            metadata: {},
            createdAt: new Date("2026-03-03T00:00:00.000Z"),
            updatedAt: new Date("2026-03-03T00:00:00.000Z"),
          },
        ]),
      },
      auditLog: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      vacation: {
        count: vi.fn().mockResolvedValue(1),
      },
      estimate: {
        count: vi.fn().mockResolvedValue(2),
      },
    };

    const result = await getDashboardOverview(db as never);

    expect(result.totalAllocations).toBe(3);
    expect(result.activeAllocations).toBe(2);
    expect(result.approvedVacations).toBe(1);
    expect(result.totalEstimates).toBe(2);
    expect(result.budgetSummary).toEqual({
      totalBudgetCents: 100_000,
      totalCostCents: 4_000,
      avgUtilizationPercent: 4,
    });
  });

  it("aggregates peak times into sorted buckets and capacity totals", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "assign_1",
            projectId: "proj_1",
            resourceId: "res_1",
            status: "PROPOSED",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 4,
            dailyCostCents: 0,
            project: { id: "proj_1", name: "Alpha", shortCode: "ALPHA", status: "ACTIVE", orderType: "FIXED" },
            resource: { id: "res_1", displayName: "Alice", chapter: "CGI" },
          },
          {
            id: "assign_2",
            projectId: "proj_2",
            resourceId: "res_2",
            status: "PROPOSED",
            startDate: new Date("2026-03-02T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 3,
            dailyCostCents: 0,
            project: { id: "proj_2", name: "Bravo", shortCode: "BRAVO", status: "ACTIVE", orderType: "FIXED" },
            resource: { id: "res_2", displayName: "Bob", chapter: "Lighting" },
          },
        ]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_1",
            displayName: "Alice",
            chapter: "CGI",
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
            countryId: "country_de",
            federalState: null,
            metroCityId: "city_1",
            country: { code: "DE" },
            metroCity: { name: "Munich" },
          },
          {
            id: "res_2",
            displayName: "Bob",
            chapter: "Lighting",
            availability: { monday: 6, tuesday: 6, wednesday: 6, thursday: 6, friday: 6 },
            countryId: "country_de",
            federalState: null,
            metroCityId: "city_2",
            country: { code: "DE" },
            metroCity: { name: "Hamburg" },
          },
        ]),
      },
    };

    const result = await getDashboardPeakTimes(db as never, {
      startDate: new Date("2026-03-01T00:00:00.000Z"),
      endDate: new Date("2026-03-03T00:00:00.000Z"),
      granularity: "month",
      groupBy: "project",
    });

    expect(result).toEqual([
      expect.objectContaining({
        period: "2026-03",
        groups: [
          { name: "ALPHA", hours: 4 },
          { name: "BRAVO", hours: 3 },
        ],
        totalHours: 7,
        capacityHours: 28,
        derivation: expect.objectContaining({
          baseAvailableHours: 28,
          effectiveAvailableHours: 28,
          publicHolidayHoursDeduction: 0,
          absenceDayEquivalent: 0,
          absenceHoursDeduction: 0,
          bookedHours: 7,
          capacityHours: 28,
          remainingCapacityHours: 21,
          overbookedHours: 0,
          utilizationPct: 25,
          groupCount: 2,
          resourceCount: 2,
        }),
      }),
    ]);
  });

  it("provides department capacity and utilization details for chapter grouping", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "assign_1",
            projectId: "proj_1",
            resourceId: "res_1",
            status: "ACTIVE",
            startDate: new Date("2026-03-02T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 8,
            dailyCostCents: 0,
            project: { id: "proj_1", name: "Alpha", shortCode: "ALPHA", status: "ACTIVE", orderType: "FIXED" },
            resource: { id: "res_1", displayName: "Alice", chapter: "CGI" },
          },
        ]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_1",
            displayName: "Alice",
            chapter: "CGI",
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
            countryId: "country_de",
            federalState: null,
            metroCityId: "city_1",
            country: { code: "DE" },
            metroCity: { name: "Munich" },
          },
          {
            id: "res_2",
            displayName: "Bob",
            chapter: "Lighting",
            availability: { monday: 4, tuesday: 4, wednesday: 4, thursday: 4, friday: 4 },
            countryId: "country_de",
            federalState: null,
            metroCityId: "city_2",
            country: { code: "DE" },
            metroCity: { name: "Hamburg" },
          },
        ]),
      },
    };

    const result = await getDashboardPeakTimes(db as never, {
      startDate: new Date("2026-03-02T00:00:00.000Z"),
      endDate: new Date("2026-03-02T00:00:00.000Z"),
      granularity: "month",
      groupBy: "chapter",
    });

    expect(result).toEqual([
      expect.objectContaining({
        period: "2026-03",
        groups: [
          expect.objectContaining({
            name: "CGI",
            hours: 8,
            capacityHours: 8,
            remainingHours: 0,
            overbookedHours: 0,
            utilizationPct: 100,
          }),
          expect.objectContaining({
            name: "Lighting",
            hours: 0,
            capacityHours: 4,
            remainingHours: 4,
            overbookedHours: 0,
            utilizationPct: 0,
          }),
        ],
        totalHours: 8,
        capacityHours: 12,
        derivation: expect.objectContaining({
          baseAvailableHours: 12,
          effectiveAvailableHours: 12,
          publicHolidayHoursDeduction: 0,
          absenceDayEquivalent: 0,
          absenceHoursDeduction: 0,
          bookedHours: 8,
          capacityHours: 12,
          remainingCapacityHours: 4,
          overbookedHours: 0,
          utilizationPct: 67,
          groupCount: 2,
          resourceCount: 2,
        }),
      }),
    ]);
  });

  it("enforces visible-role filtering for top value resources", async () => {
    const db = {
      systemSettings: {
        findUnique: vi.fn().mockResolvedValue({
          scoreVisibleRoles: ["ADMIN"],
        }),
      },
      resource: {
        findMany: vi.fn(),
      },
    };

    const hidden = await getDashboardTopValueResources(db as never, {
      limit: 10,
      userRole: "USER",
    });

    expect(hidden).toEqual([]);
    expect(db.resource.findMany).not.toHaveBeenCalled();

    db.resource.findMany.mockResolvedValue([
      {
        id: "res_1",
        eid: "alice",
        displayName: "Alice",
        chapter: "Delivery",
        valueScore: 99,
        valueScoreBreakdown: {
          skillDepth: 90,
          skillBreadth: 80,
          costEfficiency: 85,
          chargeability: 88,
          experience: 92,
          total: 99,
        },
        valueScoreUpdatedAt: new Date("2026-03-01T00:00:00.000Z"),
        lcrCents: 12_300,
        country: { code: "DE", name: "Germany" },
        federalState: "BY",
        metroCity: { name: "Munich" },
      },
    ]);

    const visible = await getDashboardTopValueResources(db as never, {
      limit: 1,
      userRole: "ADMIN",
    });

    expect(visible).toEqual([
      {
        id: "res_1",
        eid: "alice",
        displayName: "Alice",
        chapter: "Delivery",
        valueScore: 99,
        valueScoreBreakdown: {
          skillDepth: 90,
          skillBreadth: 80,
          costEfficiency: 85,
          chargeability: 88,
          experience: 92,
          total: 99,
        },
        valueScoreUpdatedAt: new Date("2026-03-01T00:00:00.000Z"),
        lcrCents: 12_300,
        countryCode: "DE",
        countryName: "Germany",
        federalState: "BY",
        metroCityName: "Munich",
      },
    ]);
    expect(db.resource.findMany).toHaveBeenCalledWith(
      expect.objectContaining({
        take: 1,
        select: expect.objectContaining({
          valueScoreBreakdown: true,
          valueScoreUpdatedAt: true,
          country: { select: { code: true, name: true } },
          federalState: true,
          metroCity: { select: { name: true } },
        }),
      }),
    );
  });

  it("keeps proposed allocations out of actual chargeability by default but can include them", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "assign_1",
            projectId: "proj_1",
            resourceId: "res_1",
            status: "PROPOSED",
            startDate: new Date("2026-03-03T00:00:00.000Z"),
            endDate: new Date("2026-03-03T00:00:00.000Z"),
            hoursPerDay: 8,
            dailyCostCents: 0,
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              status: "ACTIVE",
              orderType: "FIXED",
            },
            resource: {
              id: "res_1",
              displayName: "Alice",
              chapter: "CGI",
            },
          },
        ]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_1",
            eid: "alice",
            displayName: "Alice",
            chapter: "CGI",
            chargeabilityTarget: 80,
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
          },
        ]),
      },
    };

    const strict = await getDashboardChargeabilityOverview(db as never, {
      now: new Date("2026-03-15T00:00:00.000Z"),
      topN: 10,
      watchlistThreshold: 15,
    });
    const withProposed = await getDashboardChargeabilityOverview(db as never, {
      includeProposed: true,
      now: new Date("2026-03-15T00:00:00.000Z"),
      topN: 10,
      watchlistThreshold: 15,
    });

    expect(strict.top[0]?.actualChargeability).toBe(0);
    expect(strict.top[0]?.expectedChargeability).toBe(5);
    expect(withProposed.top[0]?.actualChargeability).toBe(5);
    expect(withProposed.top[0]?.expectedChargeability).toBe(5);
  });

  it("filters chargeability overview by departed state and country", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_1",
            eid: "alice",
            displayName: "Alice",
            chapter: "CGI",
            countryId: "country_de",
            departed: false,
            chargeabilityTarget: 80,
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
          },
        ]),
      },
    };

    const result = await getDashboardChargeabilityOverview(db as never, {
      now: new Date("2026-03-15T00:00:00.000Z"),
      topN: 10,
      watchlistThreshold: 15,
      countryIds: ["country_de"],
      departed: false,
    });

    expect(db.resource.findMany).toHaveBeenCalledWith(
      expect.objectContaining({
        where: expect.objectContaining({
          isActive: true,
          countryId: { in: ["country_de"] },
          departed: false,
        }),
      }),
    );
    expect(result.rows).toHaveLength(1);
    expect(result.top).toHaveLength(1);
    expect(result.top[0]).toEqual(
      expect.objectContaining({
        id: "res_1",
        countryId: "country_de",
        departed: false,
      }),
    );
  });

  it("includes imported TBD draft projects in actual chargeability only when proposed work is enabled", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "assign_tbd",
            projectId: "proj_tbd",
            resourceId: "res_1",
            status: "PROPOSED",
            startDate: new Date("2026-03-03T00:00:00.000Z"),
            endDate: new Date("2026-03-03T00:00:00.000Z"),
            hoursPerDay: 8,
            dailyCostCents: 0,
            project: {
              id: "proj_tbd",
              name: "TBD: AMG",
              shortCode: "TBD-AMG",
              status: "DRAFT",
              orderType: "CLIENT",
              dynamicFields: { dispoImport: { isTbd: true } },
            },
            resource: {
              id: "res_1",
              displayName: "Alice",
              chapter: "CGI",
            },
          },
        ]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_1",
            eid: "alice",
            displayName: "Alice",
            chapter: "CGI",
            chargeabilityTarget: 80,
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
          },
        ]),
      },
    };

    const strict = await getDashboardChargeabilityOverview(db as never, {
      now: new Date("2026-03-15T00:00:00.000Z"),
      topN: 10,
      watchlistThreshold: 15,
    });
    const withProposed = await getDashboardChargeabilityOverview(db as never, {
      includeProposed: true,
      now: new Date("2026-03-15T00:00:00.000Z"),
      topN: 10,
      watchlistThreshold: 15,
    });

    expect(strict.top[0]?.actualChargeability).toBe(0);
    expect(strict.top[0]?.expectedChargeability).toBe(5);
    expect(withProposed.top[0]?.actualChargeability).toBe(5);
    expect(withProposed.top[0]?.expectedChargeability).toBe(5);
  });

  it("excludes regional public holidays from dashboard chargeability availability and bookings", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "assign_holiday",
            projectId: "proj_1",
            resourceId: "res_by",
            status: "CONFIRMED",
            startDate: new Date("2026-01-06T00:00:00.000Z"),
            endDate: new Date("2026-01-06T00:00:00.000Z"),
            hoursPerDay: 8,
            dailyCostCents: 0,
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              status: "ACTIVE",
              orderType: "CLIENT",
              dynamicFields: null,
            },
            resource: {
              id: "res_by",
              displayName: "Bruce",
              chapter: "CGI",
            },
          },
        ]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_by",
            eid: "bruce.banner",
            displayName: "Bruce",
            chapter: "CGI",
            countryId: "country_de",
            federalState: "BY",
            metroCityId: "city_augsburg",
            departed: false,
            chargeabilityTarget: 80,
            country: {
              id: "country_de",
              code: "DE",
            },
            metroCity: {
              id: "city_augsburg",
              name: "Augsburg",
            },
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
          },
        ]),
      },
    };

    const result = await getDashboardChargeabilityOverview(db as never, {
      now: new Date("2026-01-15T00:00:00.000Z"),
      topN: 10,
      watchlistThreshold: 15,
    });

    expect(result.rows).toHaveLength(1);
    expect(result.top[0]?.actualChargeability).toBe(0);
    expect(result.top[0]?.expectedChargeability).toBe(0);
    expect(result.top[0]).toEqual(
      expect.objectContaining({
        countryCode: "DE",
        federalState: "BY",
        metroCityName: "Augsburg",
        derivation: expect.objectContaining({
          weeklyAvailabilityHours: 40,
          baseAvailableHours: 184,
          effectiveAvailableHours: 168,
          publicHolidayCount: 2,
          publicHolidayWorkdayCount: 2,
          publicHolidayHoursDeduction: 16,
          absenceHoursDeduction: 0,
          actualBookedHours: 0,
          expectedBookedHours: 0,
          targetBookedHours: 134.4,
          unassignedHours: 168,
        }),
      }),
    );
  });

  it("uses holiday-aware capacity in peak times for regional calendars", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "assign_1",
            projectId: "proj_1",
            resourceId: "res_by",
            status: "CONFIRMED",
            startDate: new Date("2026-01-05T00:00:00.000Z"),
            endDate: new Date("2026-01-06T00:00:00.000Z"),
            hoursPerDay: 8,
            dailyCostCents: 0,
            project: { id: "proj_1", name: "Alpha", shortCode: "ALPHA", status: "ACTIVE", orderType: "FIXED" },
            resource: { id: "res_by", displayName: "Bruce", chapter: "CGI" },
          },
        ]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_by",
            displayName: "Bruce",
            chapter: "CGI",
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
            countryId: "country_de",
            federalState: "BY",
            metroCityId: "city_munich",
            country: { code: "DE" },
            metroCity: { name: "Munich" },
          },
        ]),
      },
    };

    const result = await getDashboardPeakTimes(db as never, {
      startDate: new Date("2026-01-05T00:00:00.000Z"),
      endDate: new Date("2026-01-06T00:00:00.000Z"),
      granularity: "month",
      groupBy: "project",
    });

    expect(result).toEqual([
      expect.objectContaining({
        period: "2026-01",
        groups: [{ name: "ALPHA", hours: 8 }],
        totalHours: 8,
        capacityHours: 8,
        derivation: expect.objectContaining({
          baseAvailableHours: 16,
          effectiveAvailableHours: 8,
          publicHolidayHoursDeduction: 8,
          absenceDayEquivalent: 0,
          absenceHoursDeduction: 0,
          bookedHours: 8,
          capacityHours: 8,
          remainingCapacityHours: 0,
          overbookedHours: 0,
          utilizationPct: 100,
          groupCount: 1,
          resourceCount: 1,
        }),
      }),
    ]);
  });

  it("exposes holiday and approved-absence deductions in peak times derivation", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_by",
            displayName: "Bruce",
            chapter: "CGI",
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
            countryId: "country_de",
            federalState: "BY",
            metroCityId: "city_munich",
            country: { code: "DE" },
            metroCity: { name: "Munich" },
          },
        ]),
      },
      vacation: {
        findMany: vi.fn().mockResolvedValue([
          {
            resourceId: "res_by",
            startDate: new Date("2026-01-05T00:00:00.000Z"),
            endDate: new Date("2026-01-05T00:00:00.000Z"),
            type: "VACATION",
            isHalfDay: false,
          },
        ]),
      },
    };

    const result = await getDashboardPeakTimes(db as never, {
      startDate: new Date("2026-01-05T00:00:00.000Z"),
      endDate: new Date("2026-01-06T00:00:00.000Z"),
      granularity: "month",
      groupBy: "chapter",
    });

    expect(result).toEqual([
      expect.objectContaining({
        period: "2026-01",
        totalHours: 0,
        capacityHours: 0,
        derivation: expect.objectContaining({
          baseAvailableHours: 16,
          effectiveAvailableHours: 0,
          publicHolidayHoursDeduction: 8,
          absenceDayEquivalent: 1,
          absenceHoursDeduction: 8,
          bookedHours: 0,
          capacityHours: 0,
          remainingCapacityHours: 0,
          overbookedHours: 0,
          utilizationPct: 0,
          groupCount: 0,
          resourceCount: 1,
        }),
      }),
    ]);
  });

  it("exposes calendar context summaries in peak times derivation", async () => {
    const db = {
      assignment: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_by",
            displayName: "Bruce",
            chapter: "CGI",
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
            countryId: "country_de",
            federalState: "BY",
            metroCityId: "city_munich",
            country: { code: "DE", name: "Germany" },
            metroCity: { name: "Munich" },
          },
          {
            id: "res_hh",
            displayName: "Harvey",
            chapter: "CGI",
            availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
            countryId: "country_de",
            federalState: "HH",
            metroCityId: "city_hamburg",
            country: { code: "DE", name: "Germany" },
            metroCity: { name: "Hamburg" },
          },
        ]),
      },
    };

    const result = await getDashboardPeakTimes(db as never, {
      startDate: new Date("2026-01-05T00:00:00.000Z"),
      endDate: new Date("2026-01-06T00:00:00.000Z"),
      granularity: "month",
      groupBy: "chapter",
    });

    expect(result).toEqual([
      expect.objectContaining({
        period: "2026-01",
        derivation: expect.objectContaining({
          calendarContextCount: 2,
          calendarLocations: [
            expect.objectContaining({
              countryCode: "DE",
              countryName: "Germany",
              federalState: "HH",
              metroCityName: "Hamburg",
              resourceCount: 1,
              effectiveAvailableHours: 16,
            }),
            expect.objectContaining({
              countryCode: "DE",
              countryName: "Germany",
              federalState: "BY",
              metroCityName: "Munich",
              resourceCount: 1,
              effectiveAvailableHours: 8,
            }),
          ],
        }),
      }),
    ]);
  });

  it("does not burn budget on regional public holidays", async () => {
    const db = {
      project: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "proj_1",
            name: "Alpha",
            shortCode: "ALPHA",
            budgetCents: 10_000,
            startDate: new Date("2026-01-01T00:00:00.000Z"),
            endDate: new Date("2026-12-31T00:00:00.000Z"),
            clientId: null,
            client: null,
          },
        ]),
      },
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            projectId: "proj_1",
            startDate: new Date("2026-01-05T00:00:00.000Z"),
            endDate: new Date("2026-01-06T00:00:00.000Z"),
            dailyCostCents: 1_000,
            resource: {
              id: "res_by",
              availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
              countryId: "country_de",
              federalState: "BY",
              metroCityId: "city_munich",
              country: { code: "DE" },
              metroCity: { name: "Munich" },
            },
          },
        ]),
      },
    };

    const result = await getDashboardBudgetForecast(db as never);

    expect(result).toEqual([
      expect.objectContaining({
        projectId: "proj_1",
        shortCode: "ALPHA",
        spentCents: 1_000,
        remainingCents: 9_000,
        activeAssignmentCount: 0,
        calendarLocations: [],
      }),
    ]);
  });

  it("returns burn derivation and calendar basis for active project forecasts", async () => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2026-01-15T00:00:00.000Z"));

    try {
      const db = {
        project: {
          findMany: vi.fn().mockResolvedValue([
            {
              id: "proj_1",
              name: "Gelddruckmaschine",
              shortCode: "GDM",
              budgetCents: 100_000,
              startDate: new Date("2026-01-01T00:00:00.000Z"),
              endDate: new Date("2026-12-31T00:00:00.000Z"),
              clientId: "client_1",
              client: { name: "ACME" },
            },
          ]),
        },
        assignment: {
          findMany: vi.fn().mockResolvedValue([
            {
              projectId: "proj_1",
              startDate: new Date("2026-01-05T00:00:00.000Z"),
              endDate: new Date("2026-01-06T00:00:00.000Z"),
              dailyCostCents: 1_000,
              resource: {
                id: "res_by",
                availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
                countryId: "country_de",
                federalState: "BY",
                metroCityId: "city_munich",
                country: { code: "DE", name: "Deutschland" },
                metroCity: { name: "Munich" },
              },
            },
            {
              projectId: "proj_1",
              startDate: new Date("2026-01-15T00:00:00.000Z"),
              endDate: new Date("2026-01-16T00:00:00.000Z"),
              dailyCostCents: 2_000,
              resource: {
                id: "res_hh",
                availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
                countryId: "country_de",
                federalState: "HH",
                metroCityId: "city_hamburg",
                country: { code: "DE", name: "Deutschland" },
                metroCity: { name: "Hamburg" },
              },
            },
          ]),
        },
      };

      const result = await getDashboardBudgetForecast(db as never);

      expect(result).toEqual([
        expect.objectContaining({
          projectId: "proj_1",
          shortCode: "GDM",
          budgetCents: 100_000,
          spentCents: 5_000,
          remainingCents: 95_000,
          burnRate: 4_000,
          activeAssignmentCount: 1,
          derivation: expect.objectContaining({
            calendarContextCount: 1,
            holidayAwareAssignmentCount: 1,
            fallbackAssignmentCount: 0,
            baseBurnRateCents: 4_000,
            adjustedBurnRateCents: 4_000,
            publicHolidayDayEquivalent: 0,
            publicHolidayCostDeductionCents: 0,
            absenceDayEquivalent: 0,
            absenceCostDeductionCents: 0,
          }),
          calendarLocations: [
            expect.objectContaining({
              countryCode: "DE",
              countryName: "Deutschland",
              federalState: "HH",
              metroCityName: "Hamburg",
              activeAssignmentCount: 1,
              burnRateCents: 4_000,
            }),
          ],
        }),
      ]);
    } finally {
      vi.useRealTimers();
    }
  });

  it("excludes regional public holidays from overview budget totals", async () => {
    const db = {
      assignment: {
        findMany: vi
          .fn()
          .mockResolvedValueOnce([])
          .mockResolvedValueOnce([
            {
              startDate: new Date("2026-01-05T00:00:00.000Z"),
              endDate: new Date("2026-01-06T00:00:00.000Z"),
              dailyCostCents: 1_000,
              resource: {
                id: "res_by",
                availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
                countryId: "country_de",
                federalState: "BY",
                metroCityId: "city_munich",
                country: { code: "DE" },
                metroCity: { name: "Munich" },
              },
            },
          ]),
      },
      resource: {
        count: vi
          .fn()
          .mockResolvedValueOnce(1)
          .mockResolvedValueOnce(1),
        findMany: vi.fn().mockResolvedValue([{ chapter: "CGI", chargeabilityTarget: 80 }]),
      },
      project: {
        count: vi.fn().mockResolvedValue(1),
        findMany: vi.fn().mockResolvedValue([{ status: "ACTIVE", budgetCents: 10_000 }]),
      },
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      auditLog: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      vacation: {
        count: vi.fn().mockResolvedValue(1),
      },
      estimate: {
        count: vi.fn().mockResolvedValue(4),
      },
    };

    const result = await getDashboardOverview(db as never);

    expect(result.budgetSummary).toEqual({
      totalBudgetCents: 10_000,
      totalCostCents: 1_000,
      avgUtilizationPercent: 10,
    });
    expect(result.approvedVacations).toBe(1);
    expect(result.totalEstimates).toBe(4);
  });

  it("excludes regional public holidays from project health budget usage", async () => {
    const db = {
      project: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "proj_1",
            name: "Alpha",
            shortCode: "ALPHA",
            budgetCents: 10_000,
            endDate: new Date("2026-12-31T00:00:00.000Z"),
            clientId: null,
            client: null,
            demandRequirements: [],
          },
        ]),
      },
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            projectId: "proj_1",
            startDate: new Date("2026-01-05T00:00:00.000Z"),
            endDate: new Date("2026-01-06T00:00:00.000Z"),
            dailyCostCents: 1_000,
            resource: {
              id: "res_by",
              availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
              countryId: "country_de",
              federalState: "BY",
              metroCityId: "city_munich",
              country: { code: "DE" },
              metroCity: { name: "Munich" },
            },
          },
        ]),
      },
    };

    const result = await getDashboardProjectHealth(db as never);

    expect(result).toEqual([
      expect.objectContaining({
        shortCode: "ALPHA",
        budgetHealth: 90,
        spentCents: 1_000,
        budgetUtilizationPercent: 10,
        derivation: expect.objectContaining({
          periodStart: "2026-01-05",
          periodEnd: "2026-01-06",
          calendarContextCount: 1,
          holidayAwareAssignmentCount: 1,
          fallbackAssignmentCount: 0,
          baseSpentCents: 2_000,
          adjustedSpentCents: 1_000,
          publicHolidayDayEquivalent: 1,
          publicHolidayCostDeductionCents: 1_000,
          absenceDayEquivalent: 0,
          absenceCostDeductionCents: 0,
        }),
        calendarLocations: [
          expect.objectContaining({
            countryCode: "DE",
            federalState: "BY",
            metroCityName: "Munich",
            assignmentCount: 1,
            spentCents: 1_000,
          }),
        ],
      }),
    ]);
  });

  it("returns distinct resource counts for chapter demand grouping", async () => {
    const db = {
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "alloc_1",
            demandRequirementId: null,
            projectId: "proj_1",
            resourceId: "res_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 4,
            percentage: 50,
            role: null,
            roleId: null,
            dailyCostCents: 0,
            status: "PROPOSED",
            metadata: {},
            createdAt: new Date("2026-03-01T00:00:00.000Z"),
            updatedAt: new Date("2026-03-01T00:00:00.000Z"),
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              staffingReqs: [],
            },
            resource: { id: "res_1", displayName: "Alice", chapter: "CGI" },
          },
          {
            id: "alloc_2",
            demandRequirementId: null,
            projectId: "proj_2",
            resourceId: "res_1",
            startDate: new Date("2026-03-03T00:00:00.000Z"),
            endDate: new Date("2026-03-03T00:00:00.000Z"),
            hoursPerDay: 6,
            percentage: 75,
            role: null,
            roleId: null,
            dailyCostCents: 0,
            status: "PROPOSED",
            metadata: {},
            createdAt: new Date("2026-03-03T00:00:00.000Z"),
            updatedAt: new Date("2026-03-03T00:00:00.000Z"),
            project: {
              id: "proj_2",
              name: "Bravo",
              shortCode: "BRAVO",
              staffingReqs: [],
            },
            resource: { id: "res_1", displayName: "Alice", chapter: "CGI" },
          },
          {
            id: "alloc_3",
            demandRequirementId: null,
            projectId: "proj_2",
            resourceId: "res_2",
            startDate: new Date("2026-03-03T00:00:00.000Z"),
            endDate: new Date("2026-03-03T00:00:00.000Z"),
            hoursPerDay: 5,
            percentage: 62.5,
            role: null,
            roleId: null,
            dailyCostCents: 0,
            status: "PROPOSED",
            metadata: {},
            createdAt: new Date("2026-03-03T00:00:00.000Z"),
            updatedAt: new Date("2026-03-03T00:00:00.000Z"),
            project: {
              id: "proj_2",
              name: "Bravo",
              shortCode: "BRAVO",
              staffingReqs: [],
            },
            resource: { id: "res_2", displayName: "Bob", chapter: "CGI" },
          },
        ]),
      },
      project: {
        findMany: vi.fn().mockResolvedValue([]),
      },
    };

    const result = await getDashboardDemand(db as never, {
      startDate: new Date("2026-03-01T00:00:00.000Z"),
      endDate: new Date("2026-03-31T00:00:00.000Z"),
      groupBy: "chapter",
    });

    expect(result).toEqual([
      expect.objectContaining({
        id: "CGI",
        name: "CGI",
        shortCode: "CGI",
        allocatedHours: 19,
        requiredFTEs: 0,
        resourceCount: 2,
        derivation: expect.objectContaining({
          periodWorkingHoursBase: 176,
          requiredHours: null,
          fillPct: null,
          demandSource: "NONE",
          calendarLocations: [],
        }),
      }),
    ]);
  });

  it("prefers demand requirements and assignments for project demand semantics", async () => {
    const db = {
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "dem_1",
            projectId: "proj_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-01T00:00:00.000Z"),
            hoursPerDay: 8,
            percentage: 100,
            headcount: 2,
            status: "PROPOSED",
          },
          {
            id: "dem_2",
            projectId: "proj_1",
            startDate: new Date("2026-03-02T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 4,
            percentage: 50,
            headcount: 1,
            status: "COMPLETED",
          },
        ]),
      },
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "asn_1",
            demandRequirementId: "dem_1",
            projectId: "proj_1",
            resourceId: "res_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-01T00:00:00.000Z"),
            hoursPerDay: 8,
            percentage: 100,
            role: null,
            roleId: null,
            dailyCostCents: 0,
            status: "PROPOSED",
            metadata: {},
            createdAt: new Date("2026-03-01T00:00:00.000Z"),
            updatedAt: new Date("2026-03-01T00:00:00.000Z"),
            resource: { id: "res_1", displayName: "Alice", chapter: "CGI" },
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              staffingReqs: [{ fteCount: 9 }],
            },
          },
          {
            id: "asn_2",
            demandRequirementId: "dem_2",
            projectId: "proj_1",
            resourceId: "res_2",
            startDate: new Date("2026-03-02T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 4,
            percentage: 50,
            role: null,
            roleId: null,
            dailyCostCents: 0,
            status: "PROPOSED",
            metadata: {},
            createdAt: new Date("2026-03-02T00:00:00.000Z"),
            updatedAt: new Date("2026-03-02T00:00:00.000Z"),
            resource: { id: "res_2", displayName: "Bob", chapter: "Lighting" },
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              staffingReqs: [{ fteCount: 9 }],
            },
          },
          {
            id: "asn_3",
            demandRequirementId: null,
            projectId: "proj_1",
            resourceId: "res_3",
            startDate: new Date("2026-03-03T00:00:00.000Z"),
            endDate: new Date("2026-03-03T00:00:00.000Z"),
            hoursPerDay: 6,
            percentage: 75,
            role: null,
            roleId: null,
            dailyCostCents: 0,
            status: "PROPOSED",
            metadata: {},
            createdAt: new Date("2026-03-03T00:00:00.000Z"),
            updatedAt: new Date("2026-03-03T00:00:00.000Z"),
            resource: { id: "res_3", displayName: "Cara", chapter: "CGI" },
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              staffingReqs: [{ fteCount: 9 }],
            },
          },
        ]),
      },
      project: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "proj_1",
            name: "Alpha",
            shortCode: "ALPHA",
            staffingReqs: [{ fteCount: 9 }],
          },
        ]),
      },
    };

    const result = await getDashboardDemand(db as never, {
      startDate: new Date("2026-03-01T00:00:00.000Z"),
      endDate: new Date("2026-03-31T00:00:00.000Z"),
      groupBy: "project",
    });

    expect(result).toEqual([
      expect.objectContaining({
        id: "proj_1",
        name: "Alpha",
        shortCode: "ALPHA",
        allocatedHours: 18,
        requiredFTEs: 3.5,
        resourceCount: 3,
        derivation: expect.objectContaining({
          periodWorkingHoursBase: 176,
          requiredHours: 616,
          fillPct: 3,
          demandSource: "DEMAND_REQUIREMENTS",
          calendarLocations: [],
        }),
      }),
    ]);
  });

  it("keeps explicit project metadata when demand and assignment rows exist without legacy allocations", async () => {
    const db = {
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "dem_1",
            projectId: "proj_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-01T00:00:00.000Z"),
            hoursPerDay: 8,
            percentage: 100,
            headcount: 1,
            status: "PROPOSED",
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              staffingReqs: [],
            },
          },
        ]),
      },
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "asn_1",
            demandRequirementId: "dem_1",
            projectId: "proj_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-01T00:00:00.000Z"),
            hoursPerDay: 8,
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              staffingReqs: [],
            },
            resource: { id: "res_1", displayName: "Alice", chapter: "CGI" },
          },
        ]),
      },
      project: {
        findMany: vi.fn().mockResolvedValue([]),
      },
    };

    const result = await getDashboardDemand(db as never, {
      startDate: new Date("2026-03-01T00:00:00.000Z"),
      endDate: new Date("2026-03-31T00:00:00.000Z"),
      groupBy: "project",
    });

    expect(result).toEqual([
      expect.objectContaining({
        id: "proj_1",
        name: "Alpha",
        shortCode: "ALPHA",
        allocatedHours: 8,
        requiredFTEs: 2,
        resourceCount: 1,
        derivation: expect.objectContaining({
          periodWorkingHoursBase: 176,
          requiredHours: 352,
          fillPct: 2,
          demandSource: "DEMAND_REQUIREMENTS",
        }),
      }),
    ]);
  });

  it("falls back to staffing requirements when no demand rows exist", async () => {
    const db = {
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      assignment: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "alloc_1",
            demandRequirementId: null,
            projectId: "proj_1",
            resourceId: "res_1",
            startDate: new Date("2026-03-01T00:00:00.000Z"),
            endDate: new Date("2026-03-02T00:00:00.000Z"),
            hoursPerDay: 8,
            percentage: 100,
            role: null,
            roleId: null,
            dailyCostCents: 0,
            status: "PROPOSED",
            metadata: {},
            createdAt: new Date("2026-03-01T00:00:00.000Z"),
            updatedAt: new Date("2026-03-01T00:00:00.000Z"),
            project: {
              id: "proj_1",
              name: "Alpha",
              shortCode: "ALPHA",
              staffingReqs: [{ fteCount: 2 }],
            },
            resource: { id: "res_1", displayName: "Alice", chapter: "CGI" },
          },
        ]),
      },
      project: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "proj_1",
            name: "Alpha",
            shortCode: "ALPHA",
            staffingReqs: [{ fteCount: 2 }],
          },
        ]),
      },
    };

    const result = await getDashboardDemand(db as never, {
      startDate: new Date("2026-03-01T00:00:00.000Z"),
      endDate: new Date("2026-03-31T00:00:00.000Z"),
      groupBy: "project",
    });

    expect(result).toEqual([
      expect.objectContaining({
        id: "proj_1",
        name: "Alpha",
        shortCode: "ALPHA",
        allocatedHours: 16,
        requiredFTEs: 2,
        resourceCount: 1,
        derivation: expect.objectContaining({
          periodWorkingHoursBase: 176,
          requiredHours: 352,
          fillPct: 5,
          demandSource: "PROJECT_STAFFING_REQS",
        }),
      }),
    ]);
  });
});
