import type { Allocation, BudgetStatus, BudgetWarning } from "@capakraken/shared";
import { BUDGET_WARNING_THRESHOLDS } from "@capakraken/shared";

/**
 * Computes budget status for a project given its allocations.
 * Pure function — all data passed as parameters.
 */
export function computeBudgetStatus(
  budgetCents: number,
  winProbability: number,
  allocations: (Pick<Allocation, "status" | "dailyCostCents" | "startDate" | "endDate" | "hoursPerDay"> & {
    /** When provided (from rules engine), used instead of dailyCostCents * days */
    adjustedTotalCostCents?: number;
  })[],
  projectStartDate: Date,
  projectEndDate: Date,
): BudgetStatus {
  const activeStatuses = new Set(["CONFIRMED", "ACTIVE"]);
  const proposedStatuses = new Set(["PROPOSED"]);

  let confirmedCents = 0;
  let proposedCents = 0;

  for (const alloc of allocations) {
    const totalCents = alloc.adjustedTotalCostCents ?? (alloc.dailyCostCents * countWorkingDaysInRange(
      new Date(alloc.startDate),
      new Date(alloc.endDate),
    ));

    if (activeStatuses.has(alloc.status)) {
      confirmedCents += totalCents;
    } else if (proposedStatuses.has(alloc.status)) {
      proposedCents += totalCents;
    }
  }

  const allocatedCents = confirmedCents + proposedCents;
  const remainingCents = budgetCents - allocatedCents;
  const utilizationPercent = budgetCents > 0 ? (allocatedCents / budgetCents) * 100 : 0;
  const winProbabilityWeightedCents = Math.round(allocatedCents * (winProbability / 100));

  const warnings: BudgetWarning[] = [];

  if (utilizationPercent >= BUDGET_WARNING_THRESHOLDS.CRITICAL) {
    warnings.push({
      level: "critical",
      code: "BUDGET_CRITICAL",
      message: `Budget utilization at ${utilizationPercent.toFixed(1)}% — critical threshold exceeded`,
      thresholdPercent: BUDGET_WARNING_THRESHOLDS.CRITICAL,
      currentPercent: utilizationPercent,
    });
  } else if (utilizationPercent >= BUDGET_WARNING_THRESHOLDS.WARNING) {
    warnings.push({
      level: "warning",
      code: "BUDGET_WARNING",
      message: `Budget utilization at ${utilizationPercent.toFixed(1)}% — approaching limit`,
      thresholdPercent: BUDGET_WARNING_THRESHOLDS.WARNING,
      currentPercent: utilizationPercent,
    });
  } else if (utilizationPercent >= BUDGET_WARNING_THRESHOLDS.INFO) {
    warnings.push({
      level: "info",
      code: "BUDGET_INFO",
      message: `Budget utilization at ${utilizationPercent.toFixed(1)}%`,
      thresholdPercent: BUDGET_WARNING_THRESHOLDS.INFO,
      currentPercent: utilizationPercent,
    });
  }

  if (allocatedCents > budgetCents) {
    warnings.push({
      level: "critical",
      code: "BUDGET_EXCEEDED",
      message: `Budget exceeded by ${((allocatedCents - budgetCents) / 100).toFixed(2)} EUR`,
      thresholdPercent: 100,
      currentPercent: utilizationPercent,
    });
  }

  return {
    budgetCents,
    allocatedCents,
    confirmedCents,
    proposedCents,
    remainingCents,
    utilizationPercent,
    winProbabilityWeightedCents,
    warnings,
  };
}

/** Simple working day counter (Mon-Fri) for budget calculations */
function countWorkingDaysInRange(startDate: Date, endDate: Date): number {
  let count = 0;
  const current = new Date(startDate);
  current.setHours(0, 0, 0, 0);
  const end = new Date(endDate);
  end.setHours(0, 0, 0, 0);

  while (current <= end) {
    const dow = current.getDay();
    if (dow !== 0 && dow !== 6) {
      count++;
    }
    current.setDate(current.getDate() + 1);
  }

  return count;
}
