import { beforeEach, describe, expect, it, vi } from "vitest";
import { SystemRole } from "@capakraken/shared";
import { listAssignmentBookings } from "@capakraken/application";
import { loadResourceDailyAvailabilityContexts } from "../lib/resource-capacity.js";

vi.mock("@capakraken/application", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@capakraken/application")>();
  return {
    ...actual,
    approveEstimateVersion: vi.fn(),
    cloneEstimate: vi.fn(),
    commitDispoImportBatch: vi.fn(),
    countPlanningEntries: vi.fn().mockResolvedValue({ countsByRoleId: new Map() }),
    createEstimateExport: vi.fn(),
    createEstimatePlanningHandoff: vi.fn(),
    createEstimateRevision: vi.fn(),
    assessDispoImportReadiness: vi.fn(),
    getDashboardDemand: vi.fn().mockResolvedValue([]),
    getDashboardBudgetForecast: vi.fn().mockResolvedValue([]),
    getDashboardOverview: vi.fn(),
    getDashboardSkillGapSummary: vi.fn().mockResolvedValue({
      roleGaps: [],
      totalOpenPositions: 0,
      skillSupplyTop10: [],
      resourcesByRole: [],
    }),
    getDashboardProjectHealth: vi.fn().mockResolvedValue([]),
    getDashboardPeakTimes: vi.fn().mockResolvedValue([]),
    getDashboardTopValueResources: vi.fn().mockResolvedValue([]),
    getEstimateById: vi.fn(),
    listAssignmentBookings: vi.fn().mockResolvedValue([]),
    stageDispoImportBatch: vi.fn(),
    submitEstimateVersion: vi.fn(),
    updateEstimateDraft: vi.fn(),
  };
});

vi.mock("../lib/resource-capacity.js", async (importOriginal) => {
  const actual = await importOriginal<typeof import("../lib/resource-capacity.js")>();
  return {
    ...actual,
    calculateEffectiveAvailableHours: vi.fn(({ context }: { context?: unknown }) => (context ? 160 : 176)),
    calculateEffectiveBookedHours: vi.fn(() => 0),
    countEffectiveWorkingDays: vi.fn(({ context }: { context?: unknown }) => (context ? 20 : 22)),
    getAvailabilityHoursForDate: vi.fn(() => 8),
    loadResourceDailyAvailabilityContexts: vi.fn().mockResolvedValue(new Map()),
  };
});

import { executeTool } from "../router/assistant-tools.js";
import { createToolContext } from "./assistant-tools-report-test-helpers.js";

describe("assistant report read tools", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("routes report reads through the report router path", async () => {
    const db = {
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_1",
            displayName: "Bruce Banner",
            chapter: "Delivery",
            country: { name: "Germany" },
          },
        ]),
        count: vi.fn().mockResolvedValue(1),
      },
    };
    const ctx = createToolContext(db, { userRole: SystemRole.CONTROLLER });

    const result = await executeTool(
      "run_report",
      JSON.stringify({
        entity: "resource",
        columns: ["displayName", "chapter", "country.name"],
        filters: [{ field: "displayName", op: "contains", value: "Bruce" }],
        limit: 25,
      }),
      ctx,
    );

    expect(db.resource.findMany).toHaveBeenCalledWith({
      select: {
        id: true,
        displayName: true,
        chapter: true,
        country: { select: { name: true } },
      },
      where: {
        displayName: { contains: "Bruce", mode: "insensitive" },
      },
      take: 25,
      skip: 0,
    });
    expect(db.resource.count).toHaveBeenCalledWith({
      where: {
        displayName: { contains: "Bruce", mode: "insensitive" },
      },
    });
    expect(JSON.parse(result.content)).toEqual({
      rows: [
        {
          id: "res_1",
          displayName: "Bruce Banner",
          chapter: "Delivery",
          "country.name": "Germany",
        },
      ],
      rowCount: 1,
      totalCount: 1,
      columns: ["id", "displayName", "chapter", "country.name"],
      groups: [],
    });
  });

  it("passes report grouping and sorting options through the assistant tool", async () => {
    const db = {
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_1",
            displayName: "Bruce Banner",
            chapter: "Delivery",
          },
        ]),
        count: vi.fn().mockResolvedValue(1),
      },
    };
    const ctx = createToolContext(db, { userRole: SystemRole.CONTROLLER });

    await executeTool(
      "run_report",
      JSON.stringify({
        entity: "resource",
        columns: ["displayName", "chapter"],
        groupBy: "chapter",
        sortBy: "displayName",
        sortDir: "desc",
        limit: 25,
      }),
      ctx,
    );

    expect(db.resource.findMany).toHaveBeenCalledWith({
      select: {
        id: true,
        displayName: true,
        chapter: true,
      },
      where: {},
      orderBy: [{ chapter: "asc" }, { displayName: "desc" }],
      take: 25,
      skip: 0,
    });
  });

  it("adds explainability metadata for resource_month reports", async () => {
    vi.mocked(listAssignmentBookings).mockResolvedValue([]);
    vi.mocked(loadResourceDailyAvailabilityContexts).mockResolvedValue(new Map([
      [
        "res_1",
        {
          holidayDates: new Set(["2026-01-06"]),
          absenceFractionsByDate: new Map([["2026-01-12", 1]]),
          vacationFractionsByDate: new Map([["2026-01-12", 1]]),
        },
      ],
    ]));

    const db = {
      resource: {
        findMany: vi.fn().mockResolvedValue([
          {
            id: "res_1",
            eid: "E-1",
            displayName: "Peter Parker",
            email: "peter@example.com",
            chapter: "Delivery",
            resourceType: "EMPLOYEE",
            isActive: true,
            chgResponsibility: false,
            rolledOff: false,
            departed: false,
            lcrCents: 10000,
            ucrCents: 15000,
            currency: "EUR",
            fte: 1,
            availability: {
              monday: 8,
              tuesday: 8,
              wednesday: 8,
              thursday: 8,
              friday: 8,
              saturday: 0,
              sunday: 0,
            },
            chargeabilityTarget: 80,
            federalState: "Bayern",
            countryId: "country_de",
            metroCityId: "metro_muc",
            country: { code: "DE", name: "Germany" },
            metroCity: { name: "Munich" },
            orgUnit: { name: "Consulting" },
            managementLevelGroup: { name: "Senior", targetPercentage: 0.8 },
            managementLevel: { name: "Consultant" },
          },
        ]),
      },
    };
    const ctx = createToolContext(db, { userRole: SystemRole.CONTROLLER });

    const result = await executeTool(
      "run_report",
      JSON.stringify({
        entity: "resource_month",
        periodMonth: "2026-01",
        columns: [
          "displayName",
          "countryCode",
          "federalState",
          "metroCityName",
          "monthlyBaseAvailableHours",
          "monthlyPublicHolidayCount",
          "monthlyPublicHolidayHoursDeduction",
          "monthlyAbsenceDayEquivalent",
          "monthlyAbsenceHoursDeduction",
          "monthlySahHours",
        ],
        limit: 25,
      }),
      ctx,
    );

    expect(JSON.parse(result.content)).toEqual({
      rows: [
        {
          id: "res_1:2026-01",
          displayName: "Peter Parker",
          countryCode: "DE",
          federalState: "Bayern",
          metroCityName: "Munich",
          monthlyBaseAvailableHours: 176,
          monthlyPublicHolidayCount: 1,
          monthlyPublicHolidayHoursDeduction: 8,
          monthlyAbsenceDayEquivalent: 1,
          monthlyAbsenceHoursDeduction: 8,
          monthlySahHours: 160,
        },
      ],
      rowCount: 1,
      totalCount: 1,
      columns: [
        "id",
        "displayName",
        "countryCode",
        "federalState",
        "metroCityName",
        "monthlyBaseAvailableHours",
        "monthlyPublicHolidayCount",
        "monthlyPublicHolidayHoursDeduction",
        "monthlyAbsenceDayEquivalent",
        "monthlyAbsenceHoursDeduction",
        "monthlySahHours",
      ],
      groups: [],
      explainability: {
        entity: "resource_month",
        periodMonth: "2026-01",
        locationContextColumns: ["countryCode", "federalState", "metroCityName"],
        holidayMetricColumns: ["monthlyPublicHolidayCount", "monthlyPublicHolidayHoursDeduction"],
        absenceMetricColumns: ["monthlyAbsenceDayEquivalent", "monthlyAbsenceHoursDeduction"],
        capacityMetricColumns: ["monthlyBaseAvailableHours", "monthlySahHours"],
        chargeabilityMetricColumns: [],
        missingRecommendedColumns: [
          "countryName",
          "monthlyPublicHolidayWorkdayCount",
          "monthlyBaseWorkingDays",
          "monthlyEffectiveWorkingDays",
          "monthlyChargeabilityTargetPct",
          "monthlyTargetHours",
        ],
        notes: [
          "monthlySahHours already reflects region-specific public holidays from country, federal state, and city context when those attributes exist on the resource.",
          "monthlyAbsence* metrics only deduct workdays that are not already counted as public holidays.",
          "monthlyBaseAvailableHours shows pre-deduction capacity; compare it with holiday, absence, and SAH columns to explain the final monthly availability.",
        ],
      },
    });
  });
});
