import type { PrismaClient } from "@capakraken/db";
import { VacationStatus } from "@capakraken/db";
import { AllocationStatus } from "@capakraken/shared";
import { buildSplitAllocationReadModel } from "../allocation/build-split-allocation-read-model.js";
import { calculateInclusiveDays } from "./shared.js";
import type { WeekdayAvailability } from "@capakraken/shared";
import {
  calculateEffectiveAllocationCostCents,
  loadDailyAvailabilityContexts,
} from "./holiday-capacity.js";

function hasAvailability<T extends { availability?: unknown }>(
  resource: T | null | undefined,
): resource is T & { availability: WeekdayAvailability } {
  return resource !== null && resource !== undefined && resource.availability !== null && resource.availability !== undefined;
}

export async function getDashboardOverview(db: PrismaClient) {
  const [
    totalResources,
    activeResources,
    totalProjects,
    allProjects,
    allDemandRequirements,
    allAssignments,
    budgetAssignments,
    recentActivity,
    allResources,
    approvedVacations,
    totalEstimates,
  ] = await Promise.all([
    db.resource.count(),
    db.resource.count({ where: { isActive: true } }),
    db.project.count(),
    db.project.findMany({ select: { status: true, budgetCents: true } }),
    db.demandRequirement.findMany({
      select: {
        id: true,

        projectId: true,
        startDate: true,
        endDate: true,
        hoursPerDay: true,
        percentage: true,
        role: true,
        roleId: true,
        headcount: true,
        status: true,
        metadata: true,
        createdAt: true,
        updatedAt: true,
      },
    }),
    db.assignment.findMany({
      select: {
        id: true,

        demandRequirementId: true,
        resourceId: true,
        projectId: true,
        startDate: true,
        endDate: true,
        hoursPerDay: true,
        percentage: true,
        role: true,
        roleId: true,
        dailyCostCents: true,
        status: true,
        metadata: true,
        createdAt: true,
        updatedAt: true,
      },
    }),
    db.assignment.findMany({
      where: { status: { not: "CANCELLED" } },
      select: {
        startDate: true,
        endDate: true,
        dailyCostCents: true,
        resource: {
          select: {
            id: true,
            availability: true,
            countryId: true,
            federalState: true,
            metroCityId: true,
            country: { select: { code: true } },
            metroCity: { select: { name: true } },
          },
        },
      },
    }),
    db.auditLog.findMany({
      orderBy: { createdAt: "desc" },
      take: 10,
      select: { id: true, entityType: true, action: true, createdAt: true },
    }),
    db.resource.findMany({
      select: { chapter: true, chargeabilityTarget: true },
    }),
    db.vacation.count({ where: { status: VacationStatus.APPROVED } }),
    db.estimate.count(),
  ]);

  const planningReadModel = buildSplitAllocationReadModel({
    demandRequirements: allDemandRequirements,
    assignments: allAssignments,
  });
  const totalAllocations = planningReadModel.allocations.length;
  const activeAllocations = planningReadModel.allocations.filter(
    (allocation) => allocation.status !== AllocationStatus.CANCELLED,
  ).length;
  const contextStart = budgetAssignments.length > 0
    ? new Date(Math.min(...budgetAssignments.map((assignment) => assignment.startDate.getTime())))
    : new Date();
  const contextEnd = budgetAssignments.length > 0
    ? new Date(Math.max(...budgetAssignments.map((assignment) => assignment.endDate.getTime())))
    : new Date();
  const contexts = await loadDailyAvailabilityContexts(
    db,
    budgetAssignments
      .map((assignment) => assignment.resource)
      .filter(hasAvailability)
      .map((resource) => ({
        id: resource.id,
        availability: resource.availability as unknown as WeekdayAvailability,
        countryId: resource.countryId,
        countryCode: resource.country?.code,
        federalState: resource.federalState,
        metroCityId: resource.metroCityId,
        metroCityName: resource.metroCity?.name,
      })),
    contextStart,
    contextEnd,
  );

  const totalCostCents = budgetAssignments.reduce(
    (sum, assignment) =>
      sum + (
        hasAvailability(assignment.resource)
          ? calculateEffectiveAllocationCostCents({
            availability: assignment.resource.availability as unknown as WeekdayAvailability,
            startDate: assignment.startDate,
            endDate: assignment.endDate,
            dailyCostCents: assignment.dailyCostCents ?? 0,
            periodStart: assignment.startDate,
            periodEnd: assignment.endDate,
            context: contexts.get(assignment.resource.id),
          })
          : (assignment.dailyCostCents ?? 0) *
            calculateInclusiveDays(assignment.startDate, assignment.endDate)
      ),
    0,
  );

  const totalBudgetCents = allProjects.reduce(
    (sum, project) => sum + (project.budgetCents ?? 0),
    0,
  );
  const activeProjectCount = allProjects.filter((project) => project.status === "ACTIVE").length;
  const inactiveResourceCount = Math.max(totalResources - activeResources, 0);
  const inactiveProjectCount = Math.max(totalProjects - activeProjectCount, 0);
  const cancelledAllocations = Math.max(totalAllocations - activeAllocations, 0);
  const budgetedProjects = allProjects.filter((project) => (project.budgetCents ?? 0) > 0).length;
  const remainingBudgetCents = totalBudgetCents - totalCostCents;

  const avgUtilizationPercent =
    totalBudgetCents > 0
      ? Math.round((totalCostCents / totalBudgetCents) * 100)
      : 0;

  const statusCountMap = new Map<string, number>();
  for (const project of allProjects) {
    statusCountMap.set(
      project.status,
      (statusCountMap.get(project.status) ?? 0) + 1,
    );
  }

  const chapterMap = new Map<
    string,
    { resourceCount: number; chargeabilitySum: number }
  >();
  for (const resource of allResources) {
    const chapter = resource.chapter ?? "Unassigned";
    const existing = chapterMap.get(chapter) ?? {
      resourceCount: 0,
      chargeabilitySum: 0,
    };

    chapterMap.set(chapter, {
      resourceCount: existing.resourceCount + 1,
      chargeabilitySum:
        existing.chargeabilitySum + (resource.chargeabilityTarget ?? 0),
    });
  }

  return {
    totalResources,
    activeResources,
    inactiveResources: inactiveResourceCount,
    totalProjects,
    activeProjects: activeProjectCount,
    inactiveProjects: inactiveProjectCount,
    totalAllocations,
    activeAllocations,
    cancelledAllocations,
    approvedVacations,
    totalEstimates,
    budgetSummary: {
      totalBudgetCents,
      totalCostCents,
      avgUtilizationPercent,
    },
    budgetBasis: {
      remainingBudgetCents,
      budgetedProjects,
      unbudgetedProjects: Math.max(totalProjects - budgetedProjects, 0),
      trackedAssignmentCount: budgetAssignments.length,
      windowStart: budgetAssignments.length > 0 ? contextStart : null,
      windowEnd: budgetAssignments.length > 0 ? contextEnd : null,
    },
    recentActivity: recentActivity.map((activity) => ({
      id: activity.id,
      entityType: activity.entityType,
      action: activity.action,
      createdAt: activity.createdAt,
    })),
    projectsByStatus: [...statusCountMap.entries()].map(([status, count]) => ({
      status,
      count,
    })),
    chapterUtilization: [...chapterMap.entries()].map(([chapter, data]) => ({
      chapter,
      resourceCount: data.resourceCount,
      avgChargeabilityTarget:
        data.resourceCount > 0
          ? Math.round(data.chargeabilitySum / data.resourceCount)
          : 0,
    })),
  };
}
