"use client"

import { useEffect, useMemo, useRef } from "react"
import type { CalendarEvent, SignupStatus } from "@/lib/types"
import { STRIPE_STYLE, getEventStyle } from "@/lib/room-theme"
import { addDays, isSameDay, parseDateTime, WEEKDAY_LABELS } from "@/lib/date-utils"

const HOUR_HEIGHT = 64 // px per hour

interface WeekViewProps {
  weekStart: Date
  events: CalendarEvent[]
  myResponses: Record<number, SignupStatus | undefined>
  onEventClick: (event: CalendarEvent) => void
}

interface PositionedEvent {
  event: CalendarEvent
  start: Date
  end: Date
  top: number
  height: number
  lane: number
  lanes: number
}

// Military (24h) time, e.g. "09:00", "14:30".
function formatMilitaryTime(date: Date): string {
  const h = String(date.getHours()).padStart(2, "0")
  const m = String(date.getMinutes()).padStart(2, "0")
  return `${h}:${m}`
}

function formatHourLabel(h: number): string {
  return `${String(h % 24).padStart(2, "0")}:00`
}

// Assign overlapping events to side-by-side lanes within a single day.
function layoutDayEvents(
  events: { event: CalendarEvent; start: Date; end: Date }[],
): PositionedEvent[] {
  const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime())
  const result: PositionedEvent[] = []
  let cluster: typeof sorted = []
  let clusterEnd = 0

  const flush = () => {
    if (cluster.length === 0) return
    // Greedy lane assignment within the cluster.
    const laneEnds: number[] = []
    const withLanes = cluster.map((item) => {
      let lane = laneEnds.findIndex((end) => end <= item.start.getTime())
      if (lane === -1) {
        lane = laneEnds.length
        laneEnds.push(item.end.getTime())
      } else {
        laneEnds[lane] = item.end.getTime()
      }
      return { ...item, lane }
    })
    const lanes = laneEnds.length
    for (const item of withLanes) {
      const startHrs = item.start.getHours() + item.start.getMinutes() / 60
      const endHrs = item.end.getHours() + item.end.getMinutes() / 60
      const safeEnd = Math.max(endHrs, startHrs + 0.5)
      result.push({
        event: item.event,
        start: item.start,
        end: item.end,
        top: startHrs * HOUR_HEIGHT,
        height: (safeEnd - startHrs) * HOUR_HEIGHT,
        lane: item.lane,
        lanes,
      })
    }
    cluster = []
    clusterEnd = 0
  }

  for (const item of sorted) {
    if (cluster.length === 0 || item.start.getTime() < clusterEnd) {
      cluster.push(item)
      clusterEnd = Math.max(clusterEnd, item.end.getTime())
    } else {
      flush()
      cluster.push(item)
      clusterEnd = item.end.getTime()
    }
  }
  flush()
  return result
}

export function WeekView({ weekStart, events, myResponses, onEventClick }: WeekViewProps) {
  const scrollRef = useRef<HTMLDivElement>(null)
  const hasAutoScrolled = useRef(false)

  const days = useMemo(
    () => Array.from({ length: 7 }, (_, i) => addDays(weekStart, i)),
    [weekStart],
  )

  // Full 24-hour day, always.
  const timeSlots = useMemo(() => Array.from({ length: 24 }, (_, i) => i), [])
  const gridHeight = 24 * HOUR_HEIGHT

  const positionedByDay = useMemo(() => {
    return days.map((day) => {
      const dayEvents = events
        .map((event) => ({
          event,
          start: parseDateTime(event.start_time),
          end: parseDateTime(event.end_time),
        }))
        .filter((x) => !isNaN(x.start.getTime()) && isSameDay(x.start, day))
      return layoutDayEvents(dayEvents)
    })
  }, [days, events])

  const today = new Date()
  const nowMinutes = today.getHours() * 60 + today.getMinutes()
  const nowTop = (nowMinutes / 60) * HOUR_HEIGHT
  const todayColumnIndex = days.findIndex((d) => isSameDay(d, today))

  // Auto-scroll so the current time sits vertically centered in the
  // scrollable area, once per mount (and whenever the visible week
  // changes to include today).
  useEffect(() => {
    if (!scrollRef.current) return
    if (todayColumnIndex === -1) return
    if (hasAutoScrolled.current) return

    const container = scrollRef.current
    const target = nowTop - container.clientHeight / 2
    container.scrollTop = Math.max(0, target)
    hasAutoScrolled.current = true
  }, [todayColumnIndex, nowTop])

  return (
    <div
      ref={scrollRef}
      className="h-full overflow-auto rounded-xl border border-white/20 bg-white/20 shadow-xl backdrop-blur-lg"
    >
      {/* Week Header */}
      <div className="sticky top-0 z-10 grid grid-cols-[4rem_repeat(7,1fr)] border-b border-white/20 bg-white/10 backdrop-blur-lg">
        <div className="p-2" />
        {days.map((date, i) => {
          const isToday = isSameDay(date, today)
          return (
            <div key={i} className="border-l border-white/20 p-2 text-center">
              <div className="text-xs font-medium text-white/70">{WEEKDAY_LABELS[i]}</div>
              <div
                className={`mx-auto mt-1 flex h-8 w-8 items-center justify-center text-lg font-medium text-white ${
                  isToday ? "rounded-full bg-blue-500" : ""
                }`}
              >
                {date.getDate()}
              </div>
            </div>
          )
        })}
      </div>

      {/* Time grid */}
      <div className="relative grid grid-cols-[4rem_repeat(7,1fr)]">
        {/* Time labels */}
        <div>
          {timeSlots.map((hour) => (
            <div
              key={hour}
              className="border-b border-white/10 pr-2 text-right text-xs text-white/70"
              style={{ height: HOUR_HEIGHT }}
            >
              {formatHourLabel(hour)}
            </div>
          ))}
        </div>

        {/* Day columns */}
        {days.map((day, dayIndex) => {
          const isToday = isSameDay(day, today)
          return (
            <div
              key={dayIndex}
              className="relative border-l border-white/20"
              style={{ height: gridHeight }}
            >
              {timeSlots.map((hour) => (
                <div
                  key={hour}
                  className="border-b border-white/10"
                  style={{ height: HOUR_HEIGHT }}
                />
              ))}

              {/* Current-time indicator line */}
              {isToday && (
                <div
                  className="pointer-events-none absolute left-0 right-0 z-10 flex items-center"
                  style={{ top: nowTop }}
                >
                  <div className="-ml-1 h-2 w-2 shrink-0 rounded-full bg-red-500" />
                  <div className="h-px flex-1 bg-red-500" />
                </div>
              )}

              {positionedByDay[dayIndex].map((pe, i) => {
                const style = getEventStyle(pe.event, myResponses[pe.event.id])
                const cancelled = pe.event.status === "cancelled"
                const widthPct = 100 / pe.lanes
                return (
                  <button
                    key={pe.event.id + "-" + i}
                    onClick={() => onEventClick(pe.event)}
                    className={`animate-fade-in absolute flex flex-col items-stretch justify-start overflow-hidden rounded-md p-1.5 text-left text-xs text-white shadow-md transition-all duration-150 ease-in-out hover:z-20 hover:-translate-y-0.5 hover:shadow-lg ${style.bg} ${
                      style.dashed ? "border border-dashed border-white/70" : ""
                    } ${style.outline ? "border-2 border-white/80" : ""} ${
                      style.muted ? "opacity-60" : ""
                    }`}
                    style={{
                      top: pe.top,
                      height: pe.height,
                      left: `calc(${pe.lane * widthPct}% + 2px)`,
                      width: `calc(${widthPct}% - 4px)`,
                      ...(style.striped ? STRIPE_STYLE : {}),
                    }}
                  >
                    <div className={cancelled ? "opacity-50" : undefined}>
                      <div className={`truncate font-medium ${cancelled ? "line-through" : ""}`}>
                        {pe.event.title}
                      </div>
                      {pe.height > 34 && (
                        <div className="mt-0.5 truncate text-[10px] opacity-80">
                          {formatMilitaryTime(pe.start)}
                          {" – "}
                          {formatMilitaryTime(pe.end)}
                        </div>
                      )}
                    </div>
                  </button>
                )
              })}
            </div>
          )
        })}
      </div>
    </div>
  )
}
