import type { Allocation, WeekdayAvailability } from "@capakraken/shared";
import { getAvailableHoursForDate } from "./calculator.js";

export interface AvailabilityConflict {
  date: Date;
  requestedHours: number;
  availableHours: number;
  existingHours: number;
  overageHours: number;
}

export interface AvailabilityValidationResult {
  valid: boolean;
  conflicts: AvailabilityConflict[];
  totalConflictDays: number;
}

/**
 * Validates that a new allocation does not exceed resource availability,
 * accounting for existing allocations in the same period.
 *
 * Pure function — no DB access. All data passed as parameters.
 */
export function validateAvailability(
  startDate: Date,
  endDate: Date,
  requestedHoursPerDay: number,
  availability: WeekdayAvailability,
  existingAllocations: Pick<Allocation, "startDate" | "endDate" | "hoursPerDay" | "status">[],
  includeSaturday?: boolean,
): AvailabilityValidationResult {
  const effectiveAvailability: WeekdayAvailability = includeSaturday
    ? availability
    : { ...availability, saturday: 0 };
  const conflicts: AvailabilityConflict[] = [];

  const current = new Date(startDate);
  current.setHours(0, 0, 0, 0);
  const end = new Date(endDate);
  end.setHours(0, 0, 0, 0);

  const activeStatuses = new Set(["PROPOSED", "CONFIRMED", "ACTIVE"]);

  while (current <= end) {
    const availableHours = getAvailableHoursForDate(current, effectiveAvailability);
    if (availableHours === 0) {
      // Non-working day — skip
      current.setDate(current.getDate() + 1);
      continue;
    }

    // Sum hours from existing (non-cancelled) allocations on this day
    const existingHoursOnDay = 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 totalRequested = existingHoursOnDay + requestedHoursPerDay;

    if (totalRequested > availableHours) {
      conflicts.push({
        date: new Date(current),
        requestedHours: requestedHoursPerDay,
        availableHours,
        existingHours: existingHoursOnDay,
        overageHours: totalRequested - availableHours,
      });
    }

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

  return {
    valid: conflicts.length === 0,
    conflicts,
    totalConflictDays: conflicts.length,
  };
}

/**
 * Checks for allocation overlaps — same resource, overlapping date ranges.
 */
export function detectOverlaps(
  newStart: Date,
  newEnd: Date,
  existingAllocations: Pick<Allocation, "id" | "startDate" | "endDate" | "projectId" | "status">[],
  excludeProjectId?: string,
): string[] {
  const activeStatuses = new Set(["PROPOSED", "CONFIRMED", "ACTIVE"]);
  const overlappingIds: string[] = [];

  for (const alloc of existingAllocations) {
    if (!activeStatuses.has(alloc.status)) continue;
    if (excludeProjectId && alloc.projectId === excludeProjectId) continue;

    const aStart = new Date(alloc.startDate);
    const aEnd = new Date(alloc.endDate);

    // Ranges overlap if: newStart <= aEnd && newEnd >= aStart
    if (newStart <= aEnd && newEnd >= aStart) {
      overlappingIds.push(alloc.id);
    }
  }

  return overlappingIds;
}
