import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
import { RefreshCw } from 'lucide-react'
import type { RoomData, AppConfig, Category, FilterState, StatFilters, FlowType, ActivityEntry } from '../types'
import { useAuth } from '../components/AuthGate'
import CategoryGroup from '../components/CategoryGroup'
import { FilterBar, StatFilterBar } from '../components/FilterBar'
import ActivityPanel from '../components/ActivityPanel'
import RoomModal from '../components/RoomModal'
import CheckoutNotification, { useToasts } from '../components/CheckoutNotification'
import { hasOutstandingTasks, isDirty } from '../lib/booking-flow'
import * as api from '../api'

const POLL_INTERVAL = 60000   // rooms refresh every 60s
const EVENT_INTERVAL = 30000  // event ping every 30s
const ACTIVITY_INTERVAL = 60000

function todayStr() {
  return new Date().toISOString().slice(0, 10)
}

function formatDateLabel(dateStr: string) {
  const d = new Date(dateStr + 'T00:00:00')
  const today = todayStr()
  if (dateStr === today) return `Today — ${d.toLocaleDateString('en-GB', { weekday: 'long', day: 'numeric', month: 'short' })}`
  return d.toLocaleDateString('en-GB', { weekday: 'long', day: 'numeric', month: 'short', year: 'numeric' })
}

export default function Planner() {
  const { user } = useAuth()
  const [viewDate, setViewDate] = useState(todayStr)
  const [rooms, setRooms] = useState<RoomData[]>([])
  const [categories, setCategories] = useState<Category[]>([])
  const [config, setConfig] = useState<AppConfig | null>(null)
  const [activity, setActivity] = useState<ActivityEntry[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [refreshing, setRefreshing] = useState(false)
  const [selectedRoom, setSelectedRoom] = useState<RoomData | null>(null)

  const [filters, setFilters] = useState<FilterState>({ categories: {}, flowTypes: {} })
  const [statFilters, setStatFilters] = useState<StatFilters>({ newbookTasks: 'off', cleanDirty: 'off' })

  const lastEventTime = useRef(new Date().toISOString())
  const { toasts, addToast, dismiss: dismissToast } = useToasts(
    (config?.checkout_notification_timeout ?? 30) * 1000
  )

  const loadRooms = useCallback(async (date: string, silent = false) => {
    if (!silent) setLoading(true)
    else setRefreshing(true)
    setError(null)
    try {
      const data = await api.fetchRooms(date)
      setRooms(data.rooms)
      setCategories(data.categories)

      // Detect fresh checkouts vs prior load (depart rooms that just became dirty)
      if (silent && config) {
        const deptRooms = data.rooms.filter(r => r.flow_type === 'depart')
        deptRooms.forEach(r => {
          const prev = rooms.find(p => p.site_id === r.site_id)
          if (!prev) {
            addToast(r.site_name, 'Guest has checked out')
          }
        })
      }
    } catch (err: any) {
      setError(err.message)
    } finally {
      setLoading(false)
      setRefreshing(false)
    }
  }, [config, rooms])

  const loadActivity = useCallback(async (date: string) => {
    try {
      const data = await api.fetchActivity(date)
      setActivity(data)
    } catch { /* non-critical */ }
  }, [])

  // Initial load
  useEffect(() => {
    Promise.all([
      api.fetchConfig().then(setConfig).catch(() => {}),
    ]).then(() => {
      loadRooms(viewDate)
      loadActivity(viewDate)
    })
  }, [])

  // Reload on date change
  useEffect(() => {
    setRooms([])
    loadRooms(viewDate)
    loadActivity(viewDate)
  }, [viewDate])

  // Periodic room poll
  useEffect(() => {
    const timer = setInterval(() => loadRooms(viewDate, true), POLL_INTERVAL)
    return () => clearInterval(timer)
  }, [viewDate, loadRooms])

  // Activity poll
  useEffect(() => {
    const timer = setInterval(() => loadActivity(viewDate), ACTIVITY_INTERVAL)
    return () => clearInterval(timer)
  }, [viewDate, loadActivity])

  // Event pings poll
  useEffect(() => {
    const timer = setInterval(async () => {
      try {
        const { pings, server_time } = await api.fetchEvents(lastEventTime.current)
        lastEventTime.current = server_time
        if (pings.length > 0) {
          loadRooms(viewDate, true)
        }
      } catch { /* ignore */ }
    }, EVENT_INTERVAL)
    return () => clearInterval(timer)
  }, [viewDate, loadRooms])

  // Date navigation
  const navigate = (delta: number) => {
    const d = new Date(viewDate + 'T00:00:00Z')
    d.setUTCDate(d.getUTCDate() + delta)
    setViewDate(d.toISOString().slice(0, 10))
  }

  // Filter logic
  const toggleCategory = useCallback((id: string) => {
    setFilters(f => {
      const cur = f.categories[id] ?? 'off'
      const next = cur === 'off' ? 'inclusive' : cur === 'inclusive' ? 'exclusive' : 'off'
      return { ...f, categories: { ...f.categories, [id]: next } }
    })
  }, [])

  const toggleFlow = useCallback((flow: FlowType) => {
    setFilters(f => {
      const cur = f.flowTypes[flow] ?? 'off'
      const next = cur === 'off' ? 'inclusive' : cur === 'inclusive' ? 'exclusive' : 'off'
      return { ...f, flowTypes: { ...f.flowTypes, [flow]: next } }
    })
  }, [])

  const toggleStatFilter = useCallback((key: keyof StatFilters) => {
    setStatFilters(f => {
      const cur = f[key]
      const next = cur === 'off' ? 'show-only' : cur === 'show-only' ? 'hide' : 'off'
      return { ...f, [key]: next }
    })
  }, [])

  // Apply filters — inclusive/exclusive logic for categories and flows
  const visibleRooms = useMemo(() => {
    const inclCats = Object.entries(filters.categories).filter(([, m]) => m === 'inclusive').map(([k]) => k)
    const exclCats = Object.entries(filters.categories).filter(([, m]) => m === 'exclusive').map(([k]) => k)
    const inclFlows = Object.entries(filters.flowTypes).filter(([, m]) => m === 'inclusive').map(([k]) => k) as FlowType[]
    const exclFlows = Object.entries(filters.flowTypes).filter(([, m]) => m === 'exclusive').map(([k]) => k) as FlowType[]

    return rooms.filter(room => {
      // Category filters: if any inclusive → must be in inclusive set; exclusive → must not be in exclusive set
      if (inclCats.length && !inclCats.includes(room.category_id)) return false
      if (exclCats.includes(room.category_id)) return false

      // Flow type filters
      if (inclFlows.length && !inclFlows.includes(room.flow_type)) return false
      if (exclFlows.includes(room.flow_type)) return false

      // Stat filters
      const tasks = statFilters.newbookTasks
      if (tasks === 'show-only' && !hasOutstandingTasks(room, viewDate)) return false
      if (tasks === 'hide' && hasOutstandingTasks(room, viewDate)) return false

      const cd = statFilters.cleanDirty
      if (cd === 'show-only' && !isDirty(room)) return false
      if (cd === 'hide' && isDirty(room)) return false

      return true
    })
  }, [rooms, filters, statFilters, viewDate])

  // Group by category
  const groupedRooms = useMemo(() => {
    const map = new Map<string, RoomData[]>()
    for (const room of visibleRooms) {
      const key = room.category_id || '__none__'
      if (!map.has(key)) map.set(key, [])
      map.get(key)!.push(room)
    }
    return map
  }, [visibleRooms])

  // Modal handlers
  const handleTaskToggle = useCallback((taskId: string, completed: boolean, siteStatus: string | null) => {
    setRooms(prev => prev.map(r => ({
      ...r,
      site_status: siteStatus && r.site_id === selectedRoom?.site_id ? siteStatus : r.site_status,
      tasks: r.tasks.map(t =>
        t.task_id === taskId
          ? { ...t, completed_on: completed ? new Date().toISOString() : null }
          : t
      ),
    })))
    if (selectedRoom) {
      setSelectedRoom(prev => prev ? {
        ...prev,
        site_status: siteStatus ?? prev.site_status,
        tasks: prev.tasks.map(t =>
          t.task_id === taskId
            ? { ...t, completed_on: completed ? new Date().toISOString() : null }
            : t
        ),
      } : null)
    }
  }, [selectedRoom])

  const handleStatusUpdate = useCallback((roomId: string, status: string) => {
    setRooms(prev => prev.map(r => r.site_id === roomId ? { ...r, site_status: status } : r))
    setSelectedRoom(prev => prev?.site_id === roomId ? { ...prev, site_status: status } : prev)
    loadActivity(viewDate)
  }, [viewDate, loadActivity])

  // Render
  const orderedCategories = useMemo(() =>
    [...categories].sort((a, b) => a.order - b.order),
    [categories]
  )

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
      {/* Date bar */}
      <div className="date-bar">
        <button className="date-nav-btn" onClick={() => navigate(-1)}>←</button>
        <input
          type="date"
          value={viewDate}
          onChange={e => setViewDate(e.target.value)}
        />
        <span className="date-label">{formatDateLabel(viewDate)}</span>
        {viewDate !== todayStr() && (
          <button className="date-today-btn" onClick={() => setViewDate(todayStr())}>Today</button>
        )}
        <button
          className={`refresh-btn${refreshing ? ' spinning' : ''}`}
          onClick={() => loadRooms(viewDate, true)}
          title="Refresh"
        >
          <RefreshCw size={16} strokeWidth={1.75} />
        </button>
      </div>

      {/* Filters */}
      <FilterBar
        filters={filters}
        categories={orderedCategories}
        rooms={rooms}
        viewDate={viewDate}
        onToggleCategory={toggleCategory}
        onToggleFlow={toggleFlow}
      />
      <StatFilterBar statFilters={statFilters} onChange={toggleStatFilter} />

      {/* Main area */}
      <div className="planner-main">
        <div className="planner-rooms-area">
          {loading ? (
            <div className="loading-spinner">
              <RefreshCw size={18} strokeWidth={1.75} />
              Loading rooms…
            </div>
          ) : error ? (
            <div className="error-banner">{error}</div>
          ) : visibleRooms.length === 0 ? (
            <div className="empty-state">No rooms match the current filters</div>
          ) : (
            <div className="planner-body">
              {orderedCategories.map(cat => {
                const catRooms = groupedRooms.get(cat.id)
                if (!catRooms?.length) return null
                return (
                  <CategoryGroup
                    key={cat.id}
                    categoryId={cat.id}
                    categoryName={cat.name}
                    rooms={catRooms.sort((a, b) => a.site_order - b.site_order)}
                    viewDate={viewDate}
                    config={config}
                    onRoomClick={setSelectedRoom}
                  />
                )
              })}
              {/* Rooms without a category */}
              {groupedRooms.has('__none__') && (
                <CategoryGroup
                  categoryId="__none__"
                  categoryName="Uncategorised"
                  rooms={groupedRooms.get('__none__')!.sort((a, b) => a.site_order - b.site_order)}
                  viewDate={viewDate}
                  config={config}
                  onRoomClick={setSelectedRoom}
                />
              )}
            </div>
          )}
        </div>

        <ActivityPanel entries={activity} />
      </div>

      {/* Room modal */}
      {selectedRoom && (
        <RoomModal
          room={selectedRoom}
          viewDate={viewDate}
          config={config}
          user={user}
          onClose={() => setSelectedRoom(null)}
          onTaskToggle={handleTaskToggle}
          onStatusUpdate={handleStatusUpdate}
        />
      )}

      {/* Checkout toasts */}
      <CheckoutNotification toasts={toasts} onDismiss={dismissToast} />
    </div>
  )
}
