import { setHours, setMinutes, setSeconds, startOfDay, isBefore, addMinutes, format } from "date-fns"

export type TimeGranularity = "5min" | "10min" | "15min" | "30min" | "60min"
export type HourSlotSize = "15min" | "30min" | "1h"

export type ResourceTypeBookingFields = {
  booking_window_start: string
  booking_window_end: string
  slot_size: HourSlotSize | string
  time_granularity: TimeGranularity | string
}

/** Fallback si la fila aún no tiene columnas nuevas (pre-migración). */
export const DEFAULT_BOOKING_FIELDS: ResourceTypeBookingFields = {
  booking_window_start: "08:00:00",
  booking_window_end: "20:00:00",
  slot_size: "30min",
  time_granularity: "30min",
}

export function normalizeResourceTypeBookingFields(
  row: Partial<ResourceTypeBookingFields> | null | undefined,
): ResourceTypeBookingFields {
  if (!row) return DEFAULT_BOOKING_FIELDS
  return {
    booking_window_start: row.booking_window_start || DEFAULT_BOOKING_FIELDS.booking_window_start,
    booking_window_end: row.booking_window_end || DEFAULT_BOOKING_FIELDS.booking_window_end,
    slot_size: row.slot_size || DEFAULT_BOOKING_FIELDS.slot_size,
    time_granularity: row.time_granularity || DEFAULT_BOOKING_FIELDS.time_granularity,
  }
}

export function formatSlotEndLabel(date: Date, startHHmm: string, stepMinutes: number): string {
  const [h, m] = startHHmm.split(":").map(Number)
  const d = addMinutes(setMinutes(setHours(date, h), m), Math.max(1, stepMinutes))
  return format(d, "HH:mm")
}

export function timeGranularityToMinutes(g: string): number {
  const map: Record<string, number> = {
    "5min": 5,
    "10min": 10,
    "15min": 15,
    "30min": 30,
    "60min": 60,
  }
  return map[g] ?? 30
}

export function slotSizeToMinutes(s: string): number {
  const map: Record<string, number> = { "15min": 15, "30min": 30, "1h": 60 }
  return map[s] ?? 30
}

/** Postgres / Supabase time → minutos desde medianoche (enteros; redondea segundos). */
export function parseTimeToMinutesSinceMidnight(timeStr: string): number {
  const parts = timeStr.split(":").map((x) => parseInt(x, 10))
  const h = parts[0] ?? 0
  const m = parts[1] ?? 0
  const sec = parts[2] ?? 0
  return h * 60 + m + Math.round(sec / 60)
}

/** Inicios de celda HH:mm con paso fijo; último inicio cumple start + step <= fin de ventana. */
export function buildBookingSlotStarts(args: {
  windowStart: string
  windowEnd: string
  stepMinutes: number
}): string[] {
  const startMin = parseTimeToMinutesSinceMidnight(args.windowStart)
  const endMin = parseTimeToMinutesSinceMidnight(args.windowEnd)
  const step = Math.max(1, args.stepMinutes)
  const slots: string[] = []
  for (let t = startMin; t + step <= endMin; t += step) {
    const h = Math.floor(t / 60)
    const m = t % 60
    slots.push(`${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}`)
  }
  return slots
}

export function bookingFieldsToSlotStarts(fields: ResourceTypeBookingFields): string[] {
  const step = timeGranularityToMinutes(String(fields.time_granularity))
  return buildBookingSlotStarts({
    windowStart: fields.booking_window_start,
    windowEnd: fields.booking_window_end,
    stepMinutes: step,
  })
}

/** Unión ordenada de slots de varios tipos (cabecera de grilla multi-tipo). */
export function mergeSlotStartsForTypes(typeFieldsList: ResourceTypeBookingFields[]): string[] {
  const set = new Set<string>()
  for (const f of typeFieldsList) {
    for (const s of bookingFieldsToSlotStarts(f)) set.add(s)
  }
  return Array.from(set).sort(
    (a, b) => parseTimeToMinutesSinceMidnight(a) - parseTimeToMinutesSinceMidnight(b),
  )
}

export function slotStartIsInTypeWindow(slot: string, fields: ResourceTypeBookingFields): boolean {
  return bookingFieldsToSlotStarts(fields).includes(slot)
}

export type ReservationSlot = {
  resource_id: string
  start_at: string
  end_at: string
}

export function isSlotAvailable(
  resourceId: string,
  timeSlot: string,
  selectedDate: Date,
  reservations: ReservationSlot[],
  slotDurationMinutes: number,
): boolean {
  const resourceReservations = reservations.filter((r) => r.resource_id === resourceId)
  const [hours, minutes] = timeSlot.split(":").map(Number)
  const slotStart = setMinutes(setHours(selectedDate, hours), minutes)
  const slotEnd = addMinutes(slotStart, Math.max(1, slotDurationMinutes))

  if (isBefore(slotStart, new Date())) return false

  return !resourceReservations.some((r) => {
    const resStart = new Date(r.start_at)
    const resEnd = new Date(r.end_at)
    return slotStart < resEnd && slotEnd > resStart
  })
}

export function applyTimeStringToDate(date: Date, timeStr: string): Date {
  const parts = timeStr.split(":").map((x) => parseInt(x, 10))
  const h = parts[0] ?? 0
  const m = parts[1] ?? 0
  const s = parts[2] ?? 0
  const d = startOfDay(date)
  return setSeconds(setMinutes(setHours(d, h), m), s)
}

/** Intervalo [start, end) para “día completo” en la ventana del tipo. */
export function getFullDayBookingRange(date: Date, fields: ResourceTypeBookingFields): { start: Date; end: Date } {
  return {
    start: applyTimeStringToDate(date, fields.booking_window_start),
    end: applyTimeStringToDate(date, fields.booking_window_end),
  }
}

/** Duraciones permitidas (minutos) desde una hora de inicio hasta no pasar de windowEnd. */
export function buildDurationOptionsMinutes(args: {
  startTimeHHmm: string
  date: Date
  fields: ResourceTypeBookingFields
}): number[] {
  const { startTimeHHmm, date, fields } = args
  const step = timeGranularityToMinutes(String(fields.time_granularity))
  const minDur = slotSizeToMinutes(String(fields.slot_size))
  const start = applyTimeStringToDate(date, startTimeHHmm)
  const windowEnd = applyTimeStringToDate(date, fields.booking_window_end)
  const maxDur = Math.max(0, Math.round((windowEnd.getTime() - start.getTime()) / 60000))
  const out: number[] = []
  for (let d = minDur; d <= maxDur; d += step) {
    if (d <= maxDur) out.push(d)
  }
  return out
}

/** Compat: misma grilla que antes (8:00–19:30, paso 30). */
export const TIME_SLOTS = buildBookingSlotStarts({
  windowStart: "08:00:00",
  windowEnd: "20:00:00",
  stepMinutes: 30,
})
