import type { WeekdayAvailability } from "@capakraken/shared";
import type { TimelineAssignmentEntry } from "./TimelineContext.js";

const DAY_KEYS: (keyof WeekdayAvailability)[] = [
  "sunday",
  "monday",
  "tuesday",
  "wednesday",
  "thursday",
  "friday",
  "saturday",
];

export const DEFAULT_TIMELINE_AVAILABILITY: WeekdayAvailability = {
  sunday: 0,
  monday: 8,
  tuesday: 8,
  wednesday: 8,
  thursday: 8,
  friday: 8,
  saturday: 0,
};

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}

export function toLocalStartOfDay(value: Date | string): Date {
  const date = value instanceof Date ? new Date(value) : new Date(value);
  date.setHours(0, 0, 0, 0);
  return date;
}

export function toLocalDateKey(value: Date | string): string {
  const date = toLocalStartOfDay(value);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}

export function normalizeTimelineAvailability(value: unknown): WeekdayAvailability {
  if (!isRecord(value)) {
    return DEFAULT_TIMELINE_AVAILABILITY;
  }

  const normalized = { ...DEFAULT_TIMELINE_AVAILABILITY };
  for (const key of DAY_KEYS) {
    const next = value[key];
    if (typeof next === "number" && Number.isFinite(next)) {
      normalized[key] = next;
    }
  }
  return normalized;
}

export function getTimelineAvailabilityHoursForDate(
  availability: WeekdayAvailability,
  date: Date,
): number {
  const dayKey = DAY_KEYS[toLocalStartOfDay(date).getDay()];
  return dayKey ? (availability[dayKey] ?? 0) : 0;
}

export function getEffectiveAllocationAvailability(
  allocation: Pick<TimelineAssignmentEntry, "resource" | "metadata">,
): WeekdayAvailability {
  const availability = normalizeTimelineAvailability(allocation.resource?.availability);
  const metadata = allocation.metadata as Record<string, unknown> | null;
  const includeSaturday = metadata?.includeSaturday === true;
  return includeSaturday ? availability : { ...availability, saturday: 0 };
}

export function isAllocationScheduledOnDate(
  allocation: Pick<TimelineAssignmentEntry, "startDate" | "endDate" | "resource" | "metadata">,
  date: Date,
): boolean {
  const target = toLocalStartOfDay(date);
  const start = toLocalStartOfDay(allocation.startDate);
  const end = toLocalStartOfDay(allocation.endDate);

  if (target < start || target > end) {
    return false;
  }

  return getTimelineAvailabilityHoursForDate(getEffectiveAllocationAvailability(allocation), target) > 0;
}

export function buildAllocationWorkingDaySegments(
  allocation: Pick<TimelineAssignmentEntry, "startDate" | "endDate" | "resource" | "metadata">,
  rangeStart?: Date,
  rangeEnd?: Date,
): Array<{ start: Date; end: Date }> {
  const availability = getEffectiveAllocationAvailability(allocation);
  const start = toLocalStartOfDay(rangeStart ?? allocation.startDate);
  const end = toLocalStartOfDay(rangeEnd ?? allocation.endDate);

  if (start > end) {
    return [];
  }

  const segments: Array<{ start: Date; end: Date }> = [];
  let segmentStart: Date | null = null;
  let segmentEnd: Date | null = null;
  const cursor = new Date(start);

  while (cursor <= end) {
    const isWorkingDay = getTimelineAvailabilityHoursForDate(availability, cursor) > 0;

    if (isWorkingDay) {
      if (!segmentStart) {
        segmentStart = new Date(cursor);
      }
      segmentEnd = new Date(cursor);
    } else if (segmentStart && segmentEnd) {
      segments.push({ start: segmentStart, end: segmentEnd });
      segmentStart = null;
      segmentEnd = null;
    }

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

  if (segmentStart && segmentEnd) {
    segments.push({ start: segmentStart, end: segmentEnd });
  }

  return segments;
}
