import type {
  Allocation,
  CapacityWindow,
  Resource,
  UtilizationAnalysis,
  UtilizationPeriod,
} from "@capakraken/shared";

export interface CapacityAnalysisInput {
  resource: Pick<Resource, "id" | "displayName" | "chargeabilityTarget" | "availability">;
  allocations: (Pick<
    Allocation,
    "startDate" | "endDate" | "hoursPerDay" | "status"
  > & {
    projectName: string;
    isChargeable: boolean;
  })[];
  analysisStart: Date;
  analysisEnd: Date;
}

/**
 * Analyzes resource utilization over a given period.
 * Pure function — no DB access.
 */
export function analyzeUtilization(input: CapacityAnalysisInput): UtilizationAnalysis {
  const { resource, allocations, analysisStart, analysisEnd } = input;
  const activeStatuses = new Set(["PROPOSED", "CONFIRMED", "ACTIVE"]);

  const activeAllocs = allocations.filter((a) => activeStatuses.has(a.status));

  const periods: UtilizationPeriod[] = activeAllocs.map((a) => ({
    startDate: new Date(a.startDate),
    endDate: new Date(a.endDate),
    hoursPerDay: a.hoursPerDay,
    projectName: a.projectName,
    isChargeable: a.isChargeable,
  }));

  // Compute daily utilization to find overallocated/underutilized days
  const overallocatedDays: string[] = [];
  const underutilizedDays: string[] = [];
  let totalWorkingDays = 0;
  let totalChargeableHours = 0;
  let totalAvailableHours = 0;

  const current = new Date(analysisStart);
  current.setHours(0, 0, 0, 0);
  const end = new Date(analysisEnd);
  end.setHours(0, 0, 0, 0);

  while (current <= end) {
    const dow = current.getDay();
    const dowKey = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"][dow] as keyof typeof resource.availability;
    const availableHours = resource.availability[dowKey] ?? 0;

    if (availableHours === 0) {
      current.setDate(current.getDate() + 1);
      continue;
    }

    totalWorkingDays++;
    totalAvailableHours += availableHours;

    let allocatedHours = 0;
    let chargeableHours = 0;

    for (const alloc of activeAllocs) {
      const aStart = new Date(alloc.startDate);
      aStart.setHours(0, 0, 0, 0);
      const aEnd = new Date(alloc.endDate);
      aEnd.setHours(0, 0, 0, 0);

      if (current >= aStart && current <= aEnd) {
        allocatedHours += alloc.hoursPerDay;
        if (alloc.isChargeable) {
          chargeableHours += alloc.hoursPerDay;
        }
      }
    }

    totalChargeableHours += chargeableHours;
    const dateStr = current.toISOString().split("T")[0] ?? current.toISOString();

    if (allocatedHours > availableHours) {
      overallocatedDays.push(dateStr);
    } else if (allocatedHours < availableHours * 0.5) {
      underutilizedDays.push(dateStr);
    }

    current.setDate(current.getDate() + 1);
  }

  const currentChargeability =
    totalAvailableHours > 0 ? (totalChargeableHours / totalAvailableHours) * 100 : 0;

  return {
    resourceId: resource.id,
    resourceName: resource.displayName,
    chargeabilityTarget: resource.chargeabilityTarget,
    currentChargeability,
    chargeabilityGap: resource.chargeabilityTarget - currentChargeability,
    allocations: periods,
    overallocatedDays,
    underutilizedDays,
  };
}

/**
 * Finds capacity windows for a resource — periods where they have available hours.
 */
export function findCapacityWindows(
  resource: Pick<Resource, "id" | "displayName" | "availability">,
  existingAllocations: Pick<Allocation, "startDate" | "endDate" | "hoursPerDay" | "status">[],
  searchStart: Date,
  searchEnd: Date,
  minAvailableHoursPerDay = 4,
): CapacityWindow[] {
  const activeStatuses = new Set(["PROPOSED", "CONFIRMED", "ACTIVE"]);
  const windows: CapacityWindow[] = [];

  let windowStart: Date | null = null;
  let windowAvailableDays = 0;
  let windowTotalHours = 0;
  let windowMinHours = Infinity;

  const current = new Date(searchStart);
  current.setHours(0, 0, 0, 0);
  const end = new Date(searchEnd);
  end.setHours(0, 0, 0, 0);

  function closeWindow(closeDate: Date) {
    if (windowStart && windowAvailableDays > 0) {
      const prev = new Date(closeDate);
      prev.setDate(prev.getDate() - 1);
      windows.push({
        resourceId: resource.id,
        resourceName: resource.displayName,
        startDate: new Date(windowStart),
        endDate: prev,
        availableHoursPerDay: windowMinHours === Infinity ? 0 : windowMinHours,
        availableDays: windowAvailableDays,
        totalAvailableHours: windowTotalHours,
      });
    }
    windowStart = null;
    windowAvailableDays = 0;
    windowTotalHours = 0;
    windowMinHours = Infinity;
  }

  while (current <= end) {
    const dow = current.getDay();
    const dowKey = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"][dow] as keyof typeof resource.availability;
    const maxHours = resource.availability[dowKey] ?? 0;

    if (maxHours === 0) {
      closeWindow(current);
      current.setDate(current.getDate() + 1);
      continue;
    }

    // Sum allocated hours on this day
    const allocatedHours = existingAllocations
      .filter((a) => {
        if (!activeStatuses.has(a.status)) return false;
        const aStart = new Date(a.startDate);
        aStart.setHours(0, 0, 0, 0);
        const aEnd = new Date(a.endDate);
        aEnd.setHours(0, 0, 0, 0);
        return current >= aStart && current <= aEnd;
      })
      .reduce((sum, a) => sum + a.hoursPerDay, 0);

    const freeHours = Math.max(0, maxHours - allocatedHours);

    if (freeHours >= minAvailableHoursPerDay) {
      if (!windowStart) windowStart = new Date(current);
      windowAvailableDays++;
      windowTotalHours += freeHours;
      windowMinHours = Math.min(windowMinHours, freeHours);
    } else {
      closeWindow(current);
    }

    current.setDate(current.getDate() + 1);
  }

  closeWindow(new Date(end.getTime() + 86400000));

  return windows;
}
