export type AvailabilityStatus = 'free' | 'partial' | 'full' | 'vacation' | 'blocked'

export type ConsultantAvailability = {
  id: string
  consultant_id: string
  start_date: string
  end_date: string
  status: AvailabilityStatus
  note: string | null
}

export type TimelineIntake = {
  id: string
  intake_id: string
  supplier_name: string | null
  intake_type: string | null
  priority: 'low' | 'normal' | 'high' | 'escalation'
  preferred_first_visit_start: string | null
  preferred_first_visit_end: string | null
  preferred_second_visit_start: string | null
  preferred_second_visit_end: string | null
}

export const TIMELINE_WEEKS = 8
export const TIMELINE_DAYS = TIMELINE_WEEKS * 7

export const STATUS_PRIORITY: Record<AvailabilityStatus, number> = {
  free: 0,
  partial: 1,
  full: 2,
  vacation: 3,
  blocked: 4,
}

export const STATUS_LABEL_DE: Record<AvailabilityStatus, string> = {
  free: 'Frei',
  partial: 'Teilbelegt',
  full: 'Voll',
  vacation: 'Urlaub',
  blocked: 'Gesperrt',
}

export function parseISODate(s: string): Date {
  const d = new Date(`${s}T00:00:00Z`)
  return d
}

export function formatISODate(d: Date): string {
  const y = d.getUTCFullYear()
  const m = String(d.getUTCMonth() + 1).padStart(2, '0')
  const day = String(d.getUTCDate()).padStart(2, '0')
  return `${y}-${m}-${day}`
}

export function addDays(d: Date, days: number): Date {
  const c = new Date(d.getTime())
  c.setUTCDate(c.getUTCDate() + days)
  return c
}

export function rangeIncludesDay(start: string, end: string, day: string): boolean {
  return day >= start && day <= end
}

export function rangesOverlap(
  aStart: string,
  aEnd: string,
  bStart: string,
  bEnd: string,
): boolean {
  return aStart <= bEnd && bStart <= aEnd
}

export function aggregateDayStatus(
  entries: ConsultantAvailability[],
  consultantId: string,
  day: string,
): AvailabilityStatus | null {
  let highest: AvailabilityStatus | null = null
  for (const e of entries) {
    if (e.consultant_id !== consultantId) continue
    if (!rangeIncludesDay(e.start_date, e.end_date, day)) continue
    if (
      highest === null ||
      STATUS_PRIORITY[e.status] > STATUS_PRIORITY[highest]
    ) {
      highest = e.status
    }
  }
  return highest
}

export function findFreeConsultantIdsForRange(
  consultantIds: string[],
  entries: ConsultantAvailability[],
  rangeStart: string,
  rangeEnd: string,
): string[] {
  const blockedStatuses: ReadonlySet<AvailabilityStatus> = new Set([
    'full',
    'vacation',
    'blocked',
  ])
  const blockedSet = new Set<string>()
  for (const e of entries) {
    if (!blockedStatuses.has(e.status)) continue
    if (rangesOverlap(rangeStart, rangeEnd, e.start_date, e.end_date)) {
      blockedSet.add(e.consultant_id)
    }
  }
  return consultantIds.filter((id) => !blockedSet.has(id))
}

export type DayCell = { date: string; weekIndex: number; weekday: number }

export function buildDayCells(startDate: Date, days: number = TIMELINE_DAYS): DayCell[] {
  const cells: DayCell[] = []
  for (let i = 0; i < days; i++) {
    const d = addDays(startDate, i)
    const weekday = (d.getUTCDay() + 6) % 7
    cells.push({
      date: formatISODate(d),
      weekIndex: Math.floor(i / 7),
      weekday,
    })
  }
  return cells
}

export function startOfWeekUTC(d: Date): Date {
  const day = (d.getUTCDay() + 6) % 7
  return addDays(d, -day)
}

export type AvailabilityInput = {
  startDate: string
  endDate: string
  status: AvailabilityStatus
  note: string | null
}

export type AvailabilityValidationError =
  | 'start_required'
  | 'end_required'
  | 'end_before_start'
  | 'invalid_status'

const STATUS_VALUES: ReadonlySet<string> = new Set<string>([
  'free',
  'partial',
  'full',
  'vacation',
  'blocked',
])

export function validateAvailabilityInput(
  input: AvailabilityInput,
): { ok: true } | { ok: false; error: AvailabilityValidationError } {
  if (!input.startDate) return { ok: false, error: 'start_required' }
  if (!input.endDate) return { ok: false, error: 'end_required' }
  if (input.endDate < input.startDate) return { ok: false, error: 'end_before_start' }
  if (!STATUS_VALUES.has(input.status)) return { ok: false, error: 'invalid_status' }
  return { ok: true }
}
