/**
 * Reserved registration slots for late registrations.
 *
 * Each month gets 2 reserved slots per company:
 *   - mid-month (~15th, snapped to nearest previous working day)
 *   - end-of-month (~last day, snapped to nearest previous working day)
 *
 * Slots use the IN sequence by default. When a late IN document arrives,
 * it claims the slot's number. OUT docs get new typed numbers instead.
 */

import { v4 as uuid } from "uuid";
import { isWorkingDay } from "./working-days";
import { allocateSequenceNumber } from "./registry-service";
import type { CompanyId } from "@/core/auth/types";
import type { RegistryEntry } from "../types";

/** Snap a date to the nearest previous working day (Mon–Fri, not a holiday) */
function nearestPreviousWorkingDay(date: Date): Date {
  const d = new Date(date);
  while (!isWorkingDay(d)) {
    d.setDate(d.getDate() - 1);
  }
  return d;
}

/** Get the last calendar day of a month */
function lastDayOfMonth(year: number, month: number): Date {
  return new Date(year, month + 1, 0); // month is 0-indexed
}

/** Compute the two reservation target dates for a given month */
export function getReservationDates(
  year: number,
  month: number,
): [Date, Date] {
  const mid = nearestPreviousWorkingDay(new Date(year, month, 15));
  const end = nearestPreviousWorkingDay(lastDayOfMonth(year, month));

  // Edge case: if both land on the same day, shift mid one working day earlier
  if (mid.getTime() === end.getTime()) {
    mid.setDate(mid.getDate() - 1);
    while (!isWorkingDay(mid)) {
      mid.setDate(mid.getDate() - 1);
    }
  }

  return [mid, end];
}

/** Format a Date as YYYY-MM-DD */
function toDateStr(d: Date): string {
  return d.toISOString().slice(0, 10);
}

/**
 * Generate 2 reserved slot entries for a given company + month.
 * Allocates real IN sequence numbers so they occupy positions in the registry.
 */
export async function generateReservedSlots(
  company: CompanyId,
  year: number,
  month: number,
  actorId: string,
  actorName: string,
): Promise<RegistryEntry[]> {
  const [midDate, endDate] = getReservationDates(year, month);
  const now = new Date().toISOString();
  const slots: RegistryEntry[] = [];

  for (const targetDate of [midDate, endDate]) {
    const { number } = await allocateSequenceNumber(
      company,
      "intrat",
      year,
    );
    const dateStr = toDateStr(targetDate);

    const entry: RegistryEntry = {
      id: uuid(),
      number,
      date: dateStr,
      registrationDate: dateStr,
      direction: "intrat",
      documentType: "altele",
      subject: `[Slot rezervat] ${dateStr}`,
      sender: "",
      recipient: "",
      company,
      status: "reserved",
      linkedEntryIds: [],
      attachments: [],
      tags: [],
      notes: "",
      visibility: "internal",
      isReserved: true,
      registrationType: "normal",
      createdBy: actorId,
      createdByName: actorName,
      createdAt: now,
      updatedAt: now,
    };
    slots.push(entry);
  }

  return slots;
}

/**
 * Find an unclaimed reserved slot for a given company + month.
 * Returns the first available slot, or null if none available.
 */
export function findAvailableReservedSlot(
  entries: RegistryEntry[],
  company: CompanyId,
  targetYear: number,
  targetMonth: number,
): RegistryEntry | null {
  for (const e of entries) {
    if (!e.isReserved || e.status !== "reserved") continue;
    if (e.company !== company) continue;
    const d = new Date(e.date);
    if (d.getFullYear() === targetYear && d.getMonth() === targetMonth) {
      return e;
    }
  }
  return null;
}

/**
 * Check if reserved slots already exist for a given company + month.
 */
export function countReservedSlots(
  entries: RegistryEntry[],
  company: CompanyId,
  year: number,
  month: number,
): number {
  return entries.filter((e) => {
    if (!e.isReserved) return false;
    if (e.company !== company) return false;
    const d = new Date(e.date);
    return d.getFullYear() === year && d.getMonth() === month;
  }).length;
}
