import {
  addMinutes,
  addMonths,
  endOfDay,
  endOfMonth,
  endOfWeek,
  isAfter,
  isBefore,
  isWithinInterval,
  startOfDay,
  startOfMonth,
  startOfWeek,
} from "date-fns"

import type { Ticket, TicketPriority } from "@/lib/schemas/ticket"
import type { AgendaFilterState, AgendaPeriod } from "@/components/agenda/agenda-filters"
import { getSlaDueDate } from "@/lib/sla-utils"
import { isVisitTicket } from "@/lib/ticket-matchers"

export type AgendaSlaStatus = "on_track" | "at_risk" | "breached" | "met"
type VisitStatus = NonNullable<Ticket["visitStatus"]>
type VisitState = {
  status: VisitStatus | null
  scheduledAt: Date | null
  performedAt: Date | null
  completed: boolean
}

export type AgendaTicketSummary = {
  id: string
  reference: number
  subject: string
  queue: string | null
  company: string | null
  priority: TicketPriority
  location?: string | null
  startAt: Date | null
  endAt: Date | null
  slaStatus: AgendaSlaStatus
  visitStatus?: VisitStatus | null
  completedAt?: Date | null
  href: string
}

export type AgendaCalendarEvent = {
  id: string
  ticketId: string
  reference: number
  title: string
  queue: string | null
  priority: TicketPriority
  start: Date
  end: Date
  slaStatus: AgendaSlaStatus
  href: string
}

export type AgendaDataset = {
  range: { start: Date; end: Date }
  availableQueues: string[]
  kpis: {
    pending: number
    inProgress: number
    paused: number
    outsideSla: string
  }
  sections: {
    upcoming: AgendaTicketSummary[]
    overdue: AgendaTicketSummary[]
    unscheduled: AgendaTicketSummary[]
    completed: AgendaTicketSummary[]
  }
  calendarEvents: AgendaCalendarEvent[]
}

const DEFAULT_EVENT_DURATION_MINUTES = 60

export function buildAgendaDataset(tickets: Ticket[], filters: AgendaFilterState): AgendaDataset {
  const now = new Date()
  const range = computeRange(filters.period, now)

  const availableQueues = Array.from(
    new Set(
      tickets
        .map((ticket) => ticket.queue?.trim())
        .filter((queue): queue is string => Boolean(queue))
    )
  ).sort((a, b) => a.localeCompare(b, "pt-BR"))

  const filteredTickets = tickets
    .filter((ticket) => matchesFilters(ticket, filters))
    .filter((ticket) => isVisitTicket(ticket))

  const enriched = filteredTickets.map((ticket) => {
    const schedule = deriveScheduleWindow(ticket)
    const visitState = deriveVisitState(ticket)
    const slaStatus = computeVisitSlaStatus(visitState, now)
    return { ticket, schedule, visitState, slaStatus }
  })

  const summarySections = {
    upcoming: [] as AgendaTicketSummary[],
    overdue: [] as AgendaTicketSummary[],
    unscheduled: [] as AgendaTicketSummary[],
    completed: [] as AgendaTicketSummary[],
  }

  const upcomingWindowEnd = addMonths(now, 12)

  for (const entry of enriched) {
    const summary = buildSummary(entry.ticket, entry.schedule, entry.visitState, entry.slaStatus)
    const dueDate = entry.schedule.startAt
    const createdAt = entry.ticket.createdAt

    const isCompleted = entry.visitState.completed
    const visitCompletedAt = entry.visitState.completed ? entry.visitState.performedAt ?? entry.visitState.scheduledAt : null

    if (isCompleted) {
      if (visitCompletedAt && isWithinRange(visitCompletedAt, range)) {
        summarySections.completed.push(summary)
      }
      continue
    }

    if (dueDate) {
      const isFuture = isAfter(dueDate, now)
      if (isFuture && isBefore(dueDate, upcomingWindowEnd)) {
        summarySections.upcoming.push(summary)
      }
      if (!isFuture && isWithinRange(dueDate, range)) {
        summarySections.overdue.push(summary)
      }
    }

    if (!dueDate && entry.ticket.status !== "RESOLVED" && isWithinRange(createdAt, range)) {
      summarySections.unscheduled.push(summary)
    }
  }

  summarySections.upcoming.sort((a, b) => compareNullableDate(a.startAt, b.startAt, 1))
  summarySections.overdue.sort((a, b) => compareNullableDate(a.startAt, b.startAt, -1))
  summarySections.unscheduled.sort((a, b) => compareByPriorityThenReference(a, b))
  summarySections.completed.sort((a, b) => compareNullableDate(a.completedAt ?? null, b.completedAt ?? null, -1))

  const calendarEvents = enriched
    .filter(
      (entry): entry is typeof entry & { schedule: { startAt: Date; endAt: Date } } =>
        Boolean(entry.schedule.startAt && entry.schedule.endAt),
    )
    .map((entry) => ({
      id: `${entry.ticket.id}-event`,
      ticketId: entry.ticket.id,
      reference: entry.ticket.reference,
      title: entry.ticket.subject,
      queue: entry.ticket.queue ?? null,
      priority: entry.ticket.priority,
      start: entry.schedule.startAt!,
      end: entry.schedule.endAt!,
      slaStatus: entry.slaStatus,
      href: `/tickets/${entry.ticket.id}`,
    }))
    .sort((a, b) => a.start.getTime() - b.start.getTime())

  const outsideSlaCount = enriched.filter((entry) => entry.slaStatus === "breached" || entry.slaStatus === "at_risk").length
  const outsideSlaPct = filteredTickets.length ? Math.round((outsideSlaCount / filteredTickets.length) * 100) : 0

  const dataset: AgendaDataset = {
    range,
    availableQueues,
    kpis: {
      pending: countByStatus(filteredTickets, ["PENDING"]),
      inProgress: countByStatus(filteredTickets, ["AWAITING_ATTENDANCE"]),
      paused: countByStatus(filteredTickets, ["PAUSED"]),
      outsideSla: `${outsideSlaPct}%`,
    },
    sections: summarySections,
    calendarEvents,
  }

  return dataset
}

function matchesFilters(ticket: Ticket, filters: AgendaFilterState) {
  if (filters.queues.length > 0) {
    if (!ticket.queue) return false
    const normalizedQueue = ticket.queue.toLowerCase()
    const matchesQueue = filters.queues.some((queue) => queue.toLowerCase() === normalizedQueue)
    if (!matchesQueue) return false
  }

  if (filters.priorities.length > 0 && !filters.priorities.includes(ticket.priority)) {
    return false
  }

  if (filters.focusVisits && !isVisitTicket(ticket)) {
    return false
  }

  return true
}

function computeRange(period: AgendaPeriod, pivot: Date) {
  if (period === "today") {
    return {
      start: startOfDay(pivot),
      end: endOfDay(pivot),
    }
  }
  if (period === "month") {
    return {
      start: startOfMonth(pivot),
      end: endOfMonth(pivot),
    }
  }
  return {
    start: startOfWeek(pivot, { weekStartsOn: 1 }),
    end: endOfWeek(pivot, { weekStartsOn: 1 }),
  }
}

function deriveScheduleWindow(ticket: Ticket) {
  if (isVisitTicket(ticket) && ticket.dueAt) {
    const startAt = ticket.dueAt
    const endAt = addMinutes(startAt, DEFAULT_EVENT_DURATION_MINUTES)
    return { startAt, endAt }
  }

  const due = getSlaDueDate(ticket, "solution")
  if (!due) {
    return { startAt: null, endAt: null }
  }
  const startAt = due
  const endAt = addMinutes(startAt, DEFAULT_EVENT_DURATION_MINUTES)
  return { startAt, endAt }
}

function deriveVisitState(ticket: Ticket): VisitState {
  const scheduledAt = ticket.dueAt ?? null
  const rawStatus = (ticket.visitStatus ?? null) as VisitStatus | null
  const performedAt = ticket.visitPerformedAt ?? null
  // Se já existe registro de execução, considere concluída mesmo que o status não tenha sido persistido.
  if (!rawStatus && performedAt) {
    return { status: "done", scheduledAt, performedAt, completed: true }
  }
  if (!rawStatus) {
    return { status: null, scheduledAt, performedAt: null, completed: false }
  }
  const normalized = rawStatus.toLowerCase() as VisitStatus
  const completed = normalized === "done" || normalized === "no_show" || normalized === "canceled"
  // Se reabrir (status != concluído), zera performedAt.
  const effectivePerformed = completed ? performedAt : null
  return {
    status: normalized,
    scheduledAt,
    performedAt: effectivePerformed,
    completed,
  }
}

function computeVisitSlaStatus(visitState: VisitState, now: Date): AgendaSlaStatus {
  if (visitState.completed) {
    if (visitState.status === "done") return "met"
    if (visitState.status === "canceled") return "on_track"
    return "breached"
  }
  if (visitState.scheduledAt) {
    if (isBefore(visitState.scheduledAt, now)) {
      return "breached"
    }
    return "on_track"
  }
  return "on_track"
}

function buildSummary(
  ticket: Ticket,
  schedule: { startAt: Date | null; endAt: Date | null },
  visitState: VisitState,
  slaStatus: AgendaSlaStatus,
): AgendaTicketSummary {
  const completedAt = visitState.completed ? visitState.performedAt ?? visitState.scheduledAt : ticket.resolvedAt ?? null
  return {
    id: ticket.id,
    reference: ticket.reference,
    subject: ticket.subject,
    queue: ticket.queue ?? null,
    company: ticket.company?.name ?? null,
    priority: ticket.priority,
    location: null,
    startAt: schedule.startAt,
    endAt: ticket.resolvedAt ?? schedule.endAt,
    slaStatus,
    visitStatus: visitState.status ?? null,
    completedAt,
    href: `/tickets/${ticket.id}`,
  }
}

function isWithinRange(date: Date, range: { start: Date; end: Date }) {
  return isWithinInterval(date, range)
}

function countByStatus(tickets: Ticket[], statuses: Ticket["status"][]): number {
  const set = new Set(statuses)
  return tickets.filter((ticket) => set.has(ticket.status)).length
}

function compareNullableDate(a: Date | null, b: Date | null, direction: 1 | -1) {
  const aTime = a ? a.getTime() : Number.MAX_SAFE_INTEGER
  const bTime = b ? b.getTime() : Number.MAX_SAFE_INTEGER
  return (aTime - bTime) * direction
}

function compareByPriorityThenReference(a: AgendaTicketSummary, b: AgendaTicketSummary) {
  const rank: Record<TicketPriority, number> = { URGENT: 1, HIGH: 2, MEDIUM: 3, LOW: 4 }
  const diff = (rank[a.priority] ?? 5) - (rank[b.priority] ?? 5)
  if (diff !== 0) return diff
  return a.reference - b.reference
}
