import { prisma } from '../lib/prisma'

const BLOCKING_RESERVATION_STATUSES = ['DRAFT', 'CONFIRMED', 'ACTIVE'] as const

type AvailabilityStatus = 'AVAILABLE' | 'RESERVED' | 'MAINTENANCE' | 'UNAVAILABLE'

type DateRange = {
  startDate: Date
  endDate: Date
}

type AvailabilitySource = {
  startDate: Date
  endDate: Date
  kind: 'RESERVATION' | 'MAINTENANCE' | 'BLOCK'
}

export interface VehicleAvailabilitySummary {
  available: boolean
  status: AvailabilityStatus
  nextAvailableAt: Date | null
  blockingReason: 'RESERVATION' | 'MAINTENANCE' | 'BLOCK' | 'STATUS' | null
}

function startOfTodayUtc() {
  const now = new Date()
  return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()))
}

function overlaps(range: DateRange, source: AvailabilitySource) {
  return source.startDate < range.endDate && source.endDate > range.startDate
}

function getNextAvailableAt(anchor: Date, sources: AvailabilitySource[]) {
  const sorted = sources
    .filter((source) => source.endDate > anchor)
    .sort((a, b) => {
      const startDelta = a.startDate.getTime() - b.startDate.getTime()
      if (startDelta !== 0) return startDelta
      return a.endDate.getTime() - b.endDate.getTime()
    })

  let candidate = anchor
  let advanced = false

  for (const source of sorted) {
    if (source.endDate <= candidate) continue
    if (source.startDate > candidate) break
    candidate = new Date(Math.max(candidate.getTime(), source.endDate.getTime()))
    advanced = true
  }

  return advanced ? candidate : null
}

export async function getVehicleAvailabilitySummary(
  vehicleId: string,
  {
    range,
    includeDraftReservations = true,
    companyId,
  }: {
    range?: DateRange
    includeDraftReservations?: boolean
    companyId?: string
  } = {},
): Promise<VehicleAvailabilitySummary> {
  const vehicle = await prisma.vehicle.findFirstOrThrow({
    where: { id: vehicleId, ...(companyId ? { companyId } : {}) },
    select: { status: true },
  })

  if (vehicle.status === 'OUT_OF_SERVICE') {
    return {
      available: false,
      status: 'UNAVAILABLE',
      nextAvailableAt: null,
      blockingReason: 'STATUS',
    }
  }

  const anchor = range?.startDate ?? startOfTodayUtc()
  const reservationStatuses = includeDraftReservations
    ? [...BLOCKING_RESERVATION_STATUSES]
    : BLOCKING_RESERVATION_STATUSES.filter((status) => status !== 'DRAFT')

  const [reservations, blocks] = await Promise.all([
    prisma.reservation.findMany({
      where: {
        vehicleId,
        status: { in: reservationStatuses },
        endDate: { gt: anchor },
      },
      select: { startDate: true, endDate: true },
      orderBy: { startDate: 'asc' },
    }),
    prisma.vehicleCalendarBlock.findMany({
      where: {
        vehicleId,
        endDate: { gt: anchor },
      },
      select: { startDate: true, endDate: true, type: true },
      orderBy: { startDate: 'asc' },
    }),
  ])

  const sources: AvailabilitySource[] = [
    ...reservations.map((reservation: any) => ({
      startDate: reservation.startDate,
      endDate: reservation.endDate,
      kind: 'RESERVATION' as const,
    })),
    ...blocks.map((block: any) => ({
      startDate: block.startDate,
      endDate: block.endDate,
      kind: block.type === 'MAINTENANCE' ? 'MAINTENANCE' as const : 'BLOCK' as const,
    })),
  ]

  const overlappingSources = range ? sources.filter((source) => overlaps(range, source)) : sources.filter((source) => source.startDate <= anchor && source.endDate > anchor)
  const currentSource = overlappingSources.sort((a, b) => {
    const priority = { MAINTENANCE: 0, BLOCK: 1, RESERVATION: 2 }
    return priority[a.kind] - priority[b.kind]
  })[0]

  if (vehicle.status === 'MAINTENANCE') {
    return {
      available: false,
      status: 'MAINTENANCE',
      nextAvailableAt: getNextAvailableAt(anchor, sources) ?? null,
      blockingReason: currentSource?.kind ?? 'STATUS',
    }
  }

  if (!currentSource) {
    return {
      available: true,
      status: 'AVAILABLE',
      nextAvailableAt: null,
      blockingReason: null,
    }
  }

  const nextAvailableAnchor = currentSource.startDate > anchor ? currentSource.startDate : anchor

  return {
    available: false,
    status: currentSource.kind === 'RESERVATION' ? 'RESERVED' : currentSource.kind === 'MAINTENANCE' ? 'MAINTENANCE' : 'UNAVAILABLE',
    nextAvailableAt: getNextAvailableAt(nextAvailableAnchor, sources),
    blockingReason: currentSource.kind,
  }
}
