'use client'

import { useEffect, useMemo, useRef, useState } from 'react'
import { trpc } from '@/lib/trpc/client'
import { toast } from 'sonner'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { Skeleton } from '@/components/ui/skeleton'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog'
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from '@/components/ui/alert-dialog'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
import { Download, ExternalLink, Hotel as HotelIcon, Loader2, Plus, Trash2, Pencil } from 'lucide-react'

// Radix <SelectItem> forbids an empty-string value, so the "unassigned" option
// uses this sentinel; handleHotelChange maps it back to an unassign.
const UNASSIGN_VALUE = '__unassign__'

interface Props {
  programId: string
}

// ─── Types ────────────────────────────────────────────────────────────────────

type HotelRow = {
  id: string
  name: string
  address: string | null
  link: string | null
  notes: string | null
  _count: { stays: number }
}

type RoomingRow = {
  attendingMemberId: string
  confirmationId: string
  projectId: string
  projectTitle: string
  user: { id: string; name: string | null; email: string }
  stay: { hotelId: string; roomNumber: string | null; checkInAt: Date | null; checkOutAt: Date | null } | null
}

// ─── Helpers ──────────────────────────────────────────────────────────────────

function toDateInputValue(d: Date | null | undefined): string {
  if (!d) return ''
  const dt = new Date(d)
  if (Number.isNaN(dt.getTime())) return ''
  return dt.toISOString().slice(0, 10)
}

function fromDateInputValue(s: string): Date | null {
  if (!s) return null
  const dt = new Date(s)
  return Number.isNaN(dt.getTime()) ? null : dt
}

function csvEscape(value: string | null | undefined): string {
  const str = value ?? ''
  if (str.includes('"') || str.includes(',') || str.includes('\n')) {
    return `"${str.replace(/"/g, '""')}"`
  }
  return str
}

function buildRoomingCsv(rows: RoomingRow[], hotels: HotelRow[]): string {
  const hotelMap = new Map(hotels.map((h) => [h.id, h.name]))
  const header = ['Team', 'Member', 'Email', 'Hotel', 'Room', 'Check-in', 'Check-out'].join(',')
  const lines = rows.map((r) => {
    const s = r.stay
    return [
      csvEscape(r.projectTitle),
      csvEscape(r.user.name ?? r.user.email),
      csvEscape(r.user.email),
      csvEscape(s ? (hotelMap.get(s.hotelId) ?? '') : ''),
      csvEscape(s?.roomNumber ?? ''),
      csvEscape(s?.checkInAt ? toDateInputValue(s.checkInAt) : ''),
      csvEscape(s?.checkOutAt ? toDateInputValue(s.checkOutAt) : ''),
    ].join(',')
  })
  return [header, ...lines].join('\r\n')
}

// ─── Hotel Form Dialog ────────────────────────────────────────────────────────

type HotelFormMode = { type: 'create' } | { type: 'edit'; hotel: HotelRow }

function HotelFormDialog({
  open,
  mode,
  programId,
  onOpenChange,
}: {
  open: boolean
  mode: HotelFormMode
  programId: string
  onOpenChange: (next: boolean) => void
}) {
  const utils = trpc.useUtils()
  const [name, setName] = useState('')
  const [address, setAddress] = useState('')
  const [link, setLink] = useState('')
  const [notes, setNotes] = useState('')

  useEffect(() => {
    if (!open) return
    if (mode.type === 'edit') {
      setName(mode.hotel.name)
      setAddress(mode.hotel.address ?? '')
      setLink(mode.hotel.link ?? '')
      setNotes(mode.hotel.notes ?? '')
    } else {
      setName('')
      setAddress('')
      setLink('')
      setNotes('')
    }
  }, [open, mode])

  const onSuccess = () => {
    toast.success(mode.type === 'create' ? 'Hotel added' : 'Hotel updated')
    utils.logistics.listHotels.invalidate({ programId })
    onOpenChange(false)
  }

  const createMutation = trpc.logistics.createHotel.useMutation({
    onSuccess,
    onError: (err) => toast.error(err.message),
  })

  const updateMutation = trpc.logistics.updateHotel.useMutation({
    onSuccess,
    onError: (err) => toast.error(err.message),
  })

  const isPending = createMutation.isPending || updateMutation.isPending

  const handleSave = () => {
    if (!name.trim()) {
      toast.error('Hotel name is required')
      return
    }
    if (mode.type === 'create') {
      createMutation.mutate({
        programId,
        name: name.trim(),
        address: address.trim() || undefined,
        link: link.trim() || undefined,
        notes: notes.trim() || undefined,
      })
    } else {
      updateMutation.mutate({
        id: mode.hotel.id,
        name: name.trim(),
        address: address.trim() || null,
        link: link.trim() || null,
        notes: notes.trim() || null,
      })
    }
  }

  return (
    <Dialog open={open} onOpenChange={(next) => { if (!isPending) onOpenChange(next) }}>
      <DialogContent className="sm:max-w-md">
        <DialogHeader>
          <DialogTitle>{mode.type === 'create' ? 'Add hotel' : 'Edit hotel'}</DialogTitle>
          <DialogDescription>
            {mode.type === 'create'
              ? 'Add a hotel that finalists can be assigned to.'
              : 'Update hotel details.'}
          </DialogDescription>
        </DialogHeader>

        <div className="space-y-4">
          <div className="space-y-1.5">
            <Label htmlFor="hotel-name">Name *</Label>
            <Input
              id="hotel-name"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="Hôtel de Paris"
              required
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="hotel-address">Address</Label>
            <Textarea
              id="hotel-address"
              value={address}
              onChange={(e) => setAddress(e.target.value)}
              placeholder="Place du Casino, 98000 Monaco"
              rows={2}
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="hotel-link">Website / booking link</Label>
            <Input
              id="hotel-link"
              type="url"
              value={link}
              onChange={(e) => setLink(e.target.value)}
              placeholder="https://hoteldeparismontecarlo.com"
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="hotel-notes">Internal notes</Label>
            <Textarea
              id="hotel-notes"
              value={notes}
              onChange={(e) => setNotes(e.target.value)}
              placeholder="Check-in time, special arrangements, etc."
              rows={3}
            />
          </div>
        </div>

        <DialogFooter>
          <Button variant="outline" onClick={() => onOpenChange(false)} disabled={isPending}>
            Cancel
          </Button>
          <Button onClick={handleSave} disabled={isPending}>
            {isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
            {mode.type === 'create' ? 'Add hotel' : 'Save'}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

// ─── Hotels Section ───────────────────────────────────────────────────────────

function HotelsSection({ programId }: { programId: string }) {
  const utils = trpc.useUtils()
  const { data: hotels, isLoading } = trpc.logistics.listHotels.useQuery({ programId })
  const [dialogOpen, setDialogOpen] = useState(false)
  const [dialogMode, setDialogMode] = useState<HotelFormMode>({ type: 'create' })

  const deleteMutation = trpc.logistics.deleteHotel.useMutation({
    onSuccess: () => {
      toast.success('Hotel removed')
      utils.logistics.listHotels.invalidate({ programId })
      utils.logistics.listRooming.invalidate({ programId })
    },
    onError: (err) => toast.error(err.message),
  })

  const openCreate = () => {
    setDialogMode({ type: 'create' })
    setDialogOpen(true)
  }

  const openEdit = (hotel: HotelRow) => {
    setDialogMode({ type: 'edit', hotel })
    setDialogOpen(true)
  }

  return (
    <>
      <Card>
        <CardHeader>
          <div className="flex items-center justify-between gap-4">
            <div className="flex items-center gap-2">
              <HotelIcon className="text-muted-foreground h-4 w-4" />
              <CardTitle className="text-base">Hotels</CardTitle>
            </div>
            <Button size="sm" onClick={openCreate}>
              <Plus className="mr-1 h-4 w-4" />
              Add hotel
            </Button>
          </div>
        </CardHeader>
        <CardContent>
          {isLoading ? (
            <div className="space-y-2">
              {[1, 2].map((i) => (
                <Skeleton key={i} className="h-16 w-full" />
              ))}
            </div>
          ) : !hotels || hotels.length === 0 ? (
            <p className="text-muted-foreground py-6 text-center text-sm">
              No hotels yet. Add one above.
            </p>
          ) : (
            <div className="space-y-3">
              {hotels.map((hotel) => (
                <div
                  key={hotel.id}
                  className="flex items-start justify-between gap-4 rounded-md border p-3"
                >
                  <div className="min-w-0 flex-1 space-y-1">
                    <div className="flex items-center gap-2">
                      <span className="font-medium">{hotel.name}</span>
                      <Badge variant="secondary" className="text-xs">
                        {hotel._count.stays} guest{hotel._count.stays !== 1 ? 's' : ''}
                      </Badge>
                    </div>
                    {hotel.address && (
                      <p className="text-muted-foreground text-xs whitespace-pre-line">{hotel.address}</p>
                    )}
                    {hotel.link && (
                      <a
                        href={hotel.link}
                        target="_blank"
                        rel="noopener noreferrer"
                        className="text-primary inline-flex items-center gap-1 text-xs hover:underline"
                      >
                        Visit website <ExternalLink className="h-3 w-3" />
                      </a>
                    )}
                    {hotel.notes && (
                      <p className="text-muted-foreground text-xs italic">{hotel.notes}</p>
                    )}
                  </div>
                  <div className="flex shrink-0 items-center gap-1">
                    <Button
                      variant="ghost"
                      size="icon"
                      className="h-8 w-8"
                      onClick={() => openEdit(hotel)}
                    >
                      <Pencil className="h-4 w-4" />
                      <span className="sr-only">Edit</span>
                    </Button>
                    <AlertDialog>
                      <AlertDialogTrigger asChild>
                        <Button
                          variant="ghost"
                          size="icon"
                          className="text-destructive hover:text-destructive h-8 w-8"
                          disabled={deleteMutation.isPending}
                        >
                          <Trash2 className="h-4 w-4" />
                          <span className="sr-only">Delete</span>
                        </Button>
                      </AlertDialogTrigger>
                      <AlertDialogContent>
                        <AlertDialogHeader>
                          <AlertDialogTitle>Delete hotel?</AlertDialogTitle>
                          <AlertDialogDescription>
                            This will permanently remove <strong>{hotel.name}</strong>.
                            {hotel._count.stays > 0
                              ? ` Reassign the ${hotel._count.stays} guest(s) before deleting.`
                              : ' This action cannot be undone.'}
                          </AlertDialogDescription>
                        </AlertDialogHeader>
                        <AlertDialogFooter>
                          <AlertDialogCancel>Cancel</AlertDialogCancel>
                          <AlertDialogAction
                            className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
                            onClick={() => deleteMutation.mutate({ id: hotel.id })}
                          >
                            Delete
                          </AlertDialogAction>
                        </AlertDialogFooter>
                      </AlertDialogContent>
                    </AlertDialog>
                  </div>
                </div>
              ))}
            </div>
          )}
        </CardContent>
      </Card>

      <HotelFormDialog
        open={dialogOpen}
        mode={dialogMode}
        programId={programId}
        onOpenChange={setDialogOpen}
      />
    </>
  )
}

// ─── Attendee Row ─────────────────────────────────────────────────────────────

function AttendeeRoomRow({
  row,
  hotels,
  programId,
}: {
  row: RoomingRow
  hotels: HotelRow[]
  programId: string
}) {
  const utils = trpc.useUtils()

  const [roomNumber, setRoomNumber] = useState(row.stay?.roomNumber ?? '')
  const [checkIn, setCheckIn] = useState(toDateInputValue(row.stay?.checkInAt ?? null))
  const [checkOut, setCheckOut] = useState(toDateInputValue(row.stay?.checkOutAt ?? null))

  // Keep local state in sync when server data updates
  const prevStayRef = useRef(row.stay)
  useEffect(() => {
    const prev = prevStayRef.current
    const cur = row.stay
    // Only sync if the stay changed from outside (different hotelId or null/non-null)
    if (prev?.hotelId !== cur?.hotelId || (prev === null) !== (cur === null)) {
      setRoomNumber(cur?.roomNumber ?? '')
      setCheckIn(toDateInputValue(cur?.checkInAt ?? null))
      setCheckOut(toDateInputValue(cur?.checkOutAt ?? null))
    }
    prevStayRef.current = cur
  }, [row.stay])

  const assignMutation = trpc.logistics.assignStay.useMutation({
    onSuccess: () => utils.logistics.listRooming.invalidate({ programId }),
    onError: (err) => toast.error(err.message),
  })

  const unassignMutation = trpc.logistics.unassignStay.useMutation({
    onSuccess: () => utils.logistics.listRooming.invalidate({ programId }),
    onError: (err) => toast.error(err.message),
  })

  const currentHotelId = row.stay?.hotelId ?? ''

  const handleHotelChange = (value: string) => {
    if (!value || value === UNASSIGN_VALUE) {
      unassignMutation.mutate({ attendingMemberId: row.attendingMemberId })
    } else {
      assignMutation.mutate({
        attendingMemberId: row.attendingMemberId,
        hotelId: value,
        roomNumber: roomNumber.trim() || null,
        checkInAt: fromDateInputValue(checkIn),
        checkOutAt: fromDateInputValue(checkOut),
      })
    }
  }

  const commitRoomNumber = () => {
    if (!currentHotelId) return
    const trimmed = roomNumber.trim()
    if (trimmed === (row.stay?.roomNumber ?? '')) return
    assignMutation.mutate({
      attendingMemberId: row.attendingMemberId,
      hotelId: currentHotelId,
      roomNumber: trimmed || null,
      checkInAt: fromDateInputValue(checkIn),
      checkOutAt: fromDateInputValue(checkOut),
    })
  }

  const commitCheckIn = () => {
    if (!currentHotelId) return
    if (checkIn === toDateInputValue(row.stay?.checkInAt ?? null)) return
    assignMutation.mutate({
      attendingMemberId: row.attendingMemberId,
      hotelId: currentHotelId,
      roomNumber: roomNumber.trim() || null,
      checkInAt: fromDateInputValue(checkIn),
      checkOutAt: fromDateInputValue(checkOut),
    })
  }

  const commitCheckOut = () => {
    if (!currentHotelId) return
    if (checkOut === toDateInputValue(row.stay?.checkOutAt ?? null)) return
    assignMutation.mutate({
      attendingMemberId: row.attendingMemberId,
      hotelId: currentHotelId,
      roomNumber: roomNumber.trim() || null,
      checkInAt: fromDateInputValue(checkIn),
      checkOutAt: fromDateInputValue(checkOut),
    })
  }

  const isBusy = assignMutation.isPending || unassignMutation.isPending
  const hasHotel = !!currentHotelId

  return (
    <div className="grid grid-cols-[1fr_auto] items-center gap-2 py-2 pl-4 sm:grid-cols-[2fr_2fr_1fr_1fr_1fr]">
      {/* Member */}
      <div className="col-span-2 sm:col-span-1">
        <div className="text-sm font-medium">{row.user.name ?? row.user.email}</div>
        <div className="text-muted-foreground text-xs">{row.user.email}</div>
      </div>

      {/* Hotel select */}
      <Select value={currentHotelId} onValueChange={handleHotelChange} disabled={isBusy}>
        <SelectTrigger className="h-8 text-xs">
          <SelectValue placeholder="— Unassigned —" />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value={UNASSIGN_VALUE}>— Unassigned —</SelectItem>
          {hotels.map((h) => (
            <SelectItem key={h.id} value={h.id}>
              {h.name}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>

      {/* Room # */}
      <Input
        className="h-8 text-xs"
        placeholder="Room #"
        value={roomNumber}
        onChange={(e) => setRoomNumber(e.target.value)}
        onBlur={commitRoomNumber}
        disabled={!hasHotel || isBusy}
      />

      {/* Check-in */}
      <Input
        className="h-8 text-xs"
        type="date"
        value={checkIn}
        onChange={(e) => setCheckIn(e.target.value)}
        onBlur={commitCheckIn}
        disabled={!hasHotel || isBusy}
      />

      {/* Check-out */}
      <Input
        className="h-8 text-xs"
        type="date"
        value={checkOut}
        onChange={(e) => setCheckOut(e.target.value)}
        onBlur={commitCheckOut}
        disabled={!hasHotel || isBusy}
      />
    </div>
  )
}

// ─── Rooming Section ──────────────────────────────────────────────────────────

function RoomingSection({ programId }: { programId: string }) {
  const utils = trpc.useUtils()
  const { data: rooming, isLoading: roomingLoading } = trpc.logistics.listRooming.useQuery({ programId })
  const { data: hotels } = trpc.logistics.listHotels.useQuery({ programId })

  const assignTeamMutation = trpc.logistics.assignTeamToHotel.useMutation({
    onSuccess: () => {
      toast.success('Team assigned')
      utils.logistics.listRooming.invalidate({ programId })
      utils.logistics.listHotels.invalidate({ programId })
    },
    onError: (err) => toast.error(err.message),
  })

  // Group rows by projectTitle
  const grouped = useMemo(() => {
    if (!rooming) return []
    const map = new Map<string, { confirmationId: string; projectTitle: string; rows: RoomingRow[] }>()
    for (const row of rooming) {
      if (!map.has(row.projectId)) {
        map.set(row.projectId, {
          confirmationId: row.confirmationId,
          projectTitle: row.projectTitle,
          rows: [],
        })
      }
      map.get(row.projectId)!.rows.push(row)
    }
    return Array.from(map.values())
  }, [rooming])

  const downloadCsv = () => {
    if (!rooming || !hotels) return
    const csv = buildRoomingCsv(rooming, hotels)
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = 'rooming-manifest.csv'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
  }

  return (
    <Card>
      <CardHeader>
        <div className="flex items-center justify-between gap-4">
          <CardTitle className="text-base">Rooming</CardTitle>
          <Button
            variant="outline"
            size="sm"
            disabled={!rooming || rooming.length === 0}
            onClick={downloadCsv}
          >
            <Download className="mr-1 h-4 w-4" /> Download CSV
          </Button>
        </div>
      </CardHeader>
      <CardContent>
        {roomingLoading ? (
          <div className="space-y-2">
            {[1, 2, 3].map((i) => (
              <Skeleton key={i} className="h-12 w-full" />
            ))}
          </div>
        ) : grouped.length === 0 ? (
          <p className="text-muted-foreground py-12 text-center text-sm">
            No confirmed attendees yet.
          </p>
        ) : (
          <div className="space-y-6">
            {grouped.map((group) => (
              <div key={group.confirmationId}>
                {/* Team header */}
                <div className="bg-muted/40 flex items-center justify-between gap-4 rounded-t-md border px-3 py-2">
                  <span className="text-sm font-semibold">{group.projectTitle}</span>
                  <div className="flex items-center gap-2">
                    <span className="text-muted-foreground text-xs shrink-0">Assign whole team to</span>
                    <Select
                      value=""
                      onValueChange={(hotelId) => {
                        if (!hotelId) return
                        assignTeamMutation.mutate({
                          confirmationId: group.confirmationId,
                          hotelId,
                        })
                      }}
                      disabled={assignTeamMutation.isPending || !hotels || hotels.length === 0}
                    >
                      <SelectTrigger className="h-7 w-40 text-xs">
                        <SelectValue placeholder="Select hotel…" />
                      </SelectTrigger>
                      <SelectContent>
                        {(hotels ?? []).map((h) => (
                          <SelectItem key={h.id} value={h.id}>
                            {h.name}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  </div>
                </div>

                {/* Column headers */}
                <div className="hidden grid-cols-[2fr_2fr_1fr_1fr_1fr] gap-2 border-x border-b bg-white px-4 py-1 sm:grid">
                  <span className="text-muted-foreground text-xs font-medium">Member</span>
                  <span className="text-muted-foreground text-xs font-medium">Hotel</span>
                  <span className="text-muted-foreground text-xs font-medium">Room #</span>
                  <span className="text-muted-foreground text-xs font-medium">Check-in</span>
                  <span className="text-muted-foreground text-xs font-medium">Check-out</span>
                </div>

                {/* Attendee rows */}
                <div className="divide-y rounded-b-md border-x border-b">
                  {group.rows.map((row) => (
                    <AttendeeRoomRow
                      key={row.attendingMemberId}
                      row={row}
                      hotels={hotels ?? []}
                      programId={programId}
                    />
                  ))}
                </div>
              </div>
            ))}
          </div>
        )}
      </CardContent>
    </Card>
  )
}

// ─── Main export ──────────────────────────────────────────────────────────────

export function HotelsTab({ programId }: Props) {
  return (
    <div className="space-y-6">
      <HotelsSection programId={programId} />
      <RoomingSection programId={programId} />
    </div>
  )
}
