import { useState, useEffect, useCallback, useMemo } from 'react'
import Header        from './components/Header'
import FailedBanner  from './components/FailedBanner'
import Calendar      from './components/Calendar'
import RightPanel    from './components/RightPanel'
import SettingsModal from './components/SettingsModal'
import { api, groupScheduleItems } from './api'

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function getMondayOfWeek(date) {
  const d = new Date(date)
  const day = d.getDay()
  d.setDate(d.getDate() + (day === 0 ? -6 : 1 - day))
  d.setHours(0, 0, 0, 0)
  return d
}

function toKey(d) { return d.toISOString().slice(0, 10) }

function addDays(date, n) {
  const d = new Date(date)
  d.setDate(d.getDate() + n)
  return d
}

function getTonightWindowKeys() {
  const now = new Date()
  const hour = now.getHours()
  const today = new Date(now)
  today.setHours(0, 0, 0, 0)

  if (hour < 1) {
    return {
      shift3Date: toKey(addDays(today, -1)),
      shift1Date: toKey(today),
    }
  }

  return {
    shift3Date: toKey(today),
    shift1Date: toKey(addDays(today, 1)),
  }
}

function toTonightConfigRows(scheduleData) {
  const { shift3Date, shift1Date } = getTonightWindowKeys()
  const tonightTests = [
    ...((scheduleData[shift3Date]?.shift3) ?? []),
    ...((scheduleData[shift1Date]?.shift1) ?? []),
  ]

  const bySta = new Map()

  for (const test of tonightTests) {
    const config = test.config ?? {}
    for (const entry of Object.values(config)) {
      const staRaw = entry?.sta
      const testPoint = entry?.test_point
      if (!staRaw || !testPoint) continue

      for (const staPart of String(staRaw).split(',')) {
        const sta = staPart.trim().toUpperCase()
        if (!sta) continue

        if (!bySta.has(sta)) {
          bySta.set(sta, { sta, points: new Set() })
        }
        bySta.get(sta).points.add(testPoint)
      }
    }
  }

  return Array.from(bySta.values())
    .map((row) => ({
      sta: row.sta,
      testPoints: Array.from(row.points).sort(),
    }))
    .sort((a, b) => a.sta.localeCompare(b.sta))
}

const DEFAULT_SETTINGS = {
  p2pCoeCsvPath:      '',
  p3pCsvPath:         '',
  refResultDir:       '',
  dutResultDir:       '',
  testExclusion:      '',
  holidays:           '',
  startDateOverride:  '',
}

// ---------------------------------------------------------------------------

export default function App() {
  const [settingsOpen,   setSettingsOpen]   = useState(false)
  const [settings,       setSettings]       = useState(DEFAULT_SETTINGS)
  const [daytimeEnabled, setDaytimeEnabled] = useState(false)
  const [topPriority,    setTopPriority]    = useState('')
  const [lowestPriority, setLowestPriority] = useState('')
  const [failedTests,    setFailedTests]    = useState([])
  const [scheduleData,   setScheduleData]   = useState({})
  const [completionDate, setCompletionDate] = useState(null)
  const [weekStart,      setWeekStart]      = useState(() => getMondayOfWeek(new Date()))
  const [loading,        setLoading]        = useState(false)
  const [error,          setError]          = useState(null)
  const tonightConfigRows = useMemo(() => toTonightConfigRows(scheduleData), [scheduleData])

  // Fetch schedule for the given weekStart (Monday)
  const fetchSchedule = useCallback(async (start) => {
    try {
      const data = await api.getScheduleWeek(toKey(start))
      setScheduleData(groupScheduleItems(data.items))
    } catch (e) {
      console.error('Failed to fetch schedule:', e)
    }
  }, [])

  // On mount: load settings + holidays + schedule for today's week
  useEffect(() => {
    async function init() {
      try {
        const [saved, holidayData] = await Promise.all([
          api.getSettings(),
          api.getHolidays(),
        ])
        setSettings(prev => ({
          ...prev,
          ...saved,
          holidays: (holidayData.dates ?? []).join(', '),
        }))
      } catch (e) {
        console.warn('Backend not reachable on load:', e.message)
      }
      await fetchSchedule(getMondayOfWeek(new Date()))
    }
    init()
  }, [fetchSchedule])

  // Refetch whenever the displayed week changes
  useEffect(() => {
    fetchSchedule(weekStart)
  }, [weekStart, fetchSchedule])

  // -------------------------------------------------------------------------

  async function handleSaveSettings(newSettings) {
    setLoading(true)
    setError(null)
    try {
      await api.saveSettings(newSettings)

      const holidayDates = (newSettings.holidays ?? '')
        .split(',').map(s => s.trim()).filter(Boolean)
      await api.saveHolidays(holidayDates)

      if (newSettings.p2pCoeCsvPath?.trim()) {
        await api.loadCsv(newSettings.p2pCoeCsvPath.trim())
      }
      if (newSettings.p3pCsvPath?.trim()) {
        await api.loadCsv(newSettings.p3pCsvPath.trim())
      }

      setSettings(newSettings)
    } catch (e) {
      setError(e.message)
    } finally {
      setLoading(false)
    }
  }

  async function handleRemakeSchedule() {
    setLoading(true)
    setError(null)
    try {
      const result = await api.compileSchedule({
        start_date: settings.startDateOverride?.trim() || null,
        daytime_testing_today: daytimeEnabled,
        top_priority_tests: topPriority.split(',').map(s => s.trim()).filter(Boolean),
        lowest_priority_tests: lowestPriority.split(',').map(s => s.trim()).filter(Boolean),
        rule: settings.testExclusion ?? '',
      })
      setCompletionDate(result.completion_date ?? null)
      await fetchSchedule(weekStart)
    } catch (e) {
      setError(e.message)
    } finally {
      setLoading(false)
    }
  }

  function handleRerunDecision(rerunDuringDay) {
    // TODO: POST /api/failed-tests/rerun-decision once backend endpoint exists
    console.log('Rerun during day:', rerunDuringDay)
    setFailedTests([])
  }

  // -------------------------------------------------------------------------

  return (
    <div className="flex flex-col min-h-screen bg-gray-950 text-gray-100">
      <Header onOpenSettings={() => setSettingsOpen(true)} />

      {error && (
        <div className="px-6 py-2 bg-orange-900/60 border-b border-orange-700 text-orange-200 text-sm flex items-center justify-between">
          <span>{error}</span>
          <button className="ml-3 text-orange-400 hover:text-white" onClick={() => setError(null)}>✕</button>
        </div>
      )}

      <FailedBanner
        failedTests={failedTests}
        estimatedMinutes={0}
        onDecision={handleRerunDecision}
      />

      <main className="flex flex-1 gap-4 p-4 overflow-hidden">
        <div className="flex-1 min-w-0 flex flex-col">
          <Calendar
            scheduleData={scheduleData}
            daytimeDateKey={daytimeEnabled ? toKey(new Date()) : null}
            weekStart={weekStart}
            onWeekChange={setWeekStart}
          />
        </div>

        <RightPanel
          completionDate={completionDate}
          daytimeEnabled={daytimeEnabled}
          onDaytimeEnabledChange={setDaytimeEnabled}
          topPriority={topPriority}
          onTopPriorityChange={setTopPriority}
          lowestPriority={lowestPriority}
          onLowestPriorityChange={setLowestPriority}
          onRemakeSchedule={handleRemakeSchedule}
          loading={loading}
          tonightConfigRows={tonightConfigRows}
        />
      </main>

      <SettingsModal
        isOpen={settingsOpen}
        onClose={() => setSettingsOpen(false)}
        settings={settings}
        onSave={handleSaveSettings}
      />
    </div>
  )
}
