import { useCallback, useEffect, useState } from 'react'
import { useFrameViewport } from '../hooks/useFrameViewport'
import { Save, RefreshCw, Camera, Trash2, Printer, Repeat2 } from 'lucide-react'
import { useAuth } from '../components/AuthGate'
import { dfGetWorksheet, dfSaveWorksheet, dfGetReport, dfSaveSnapshot, dfDeleteSnapshot } from '../api'
import type { WorksheetDay, WorksheetData, ForecastReportData, ForecastSnapshot } from '../types'
import { can, DOW_NAMES, MONTH_NAMES } from '../types'

// ── formatters ────────────────────────────────────────────────────────────────

const fmtCcy = (n: number | null | undefined) =>
  n == null ? '—' : `£${n.toLocaleString('en-GB', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`

const fmtPct = (n: number | null | undefined) =>
  n == null ? '—' : `${n.toFixed(1)}%`

const fmtNum = (n: number | null | undefined, d = 0) =>
  n == null ? '—' : n.toLocaleString('en-GB', { minimumFractionDigits: d, maximumFractionDigits: d })

// ── MonthNav ──────────────────────────────────────────────────────────────────

function MonthNav({ year, month, onChange }: { year: number; month: number; onChange: (y: number, m: number) => void }) {
  const prev = () => month === 1 ? onChange(year - 1, 12) : onChange(year, month - 1)
  const next = () => month === 12 ? onChange(year + 1, 1) : onChange(year, month + 1)
  return (
    <div className="df-month-nav">
      <button className="df-nav-btn" onClick={prev}>‹</button>
      <span className="df-month-label">{MONTH_NAMES[month - 1]} {year}</span>
      <button className="df-nav-btn" onClick={next}>›</button>
    </div>
  )
}

// ── Override state helpers ────────────────────────────────────────────────────

type DayOverride = {
  pickup_rooms: string
  pickup_avg_rate: string
  dry_override: string
  wet_override: string
}

// ── Worksheet tab ─────────────────────────────────────────────────────────────

function WorksheetTab({ year, month }: { year: number; month: number }) {
  const { user } = useAuth()
  const canEdit = can(user, 'edit')

  const [data, setData]           = useState<WorksheetData | null>(null)
  const [loading, setLoading]     = useState(true)
  const [saving, setSaving]       = useState(false)
  const [error, setError]         = useState<string | null>(null)
  const [sessionRate, setRate]    = useState('135')
  const [overrides, setOverrides] = useState<Record<string, DayOverride>>({})
  const [dirty, setDirty]         = useState(false)

  const load = useCallback(async () => {
    setLoading(true); setError(null)
    try {
      const d: WorksheetData = await dfGetWorksheet(year, month)
      setData(d)
      setRate(String(d.session.pickup_avg_rate ?? 135))
      const ovrs: Record<string, DayOverride> = {}
      for (const day of d.days) {
        ovrs[day.date] = {
          pickup_rooms:    day.pickup_rooms > 0 ? String(day.pickup_rooms) : '',
          pickup_avg_rate: day.pickup_avg_rate != null ? String(day.pickup_avg_rate) : '',
          dry_override:    day.dry_override != null ? String(day.dry_override) : '',
          wet_override:    day.wet_override != null ? String(day.wet_override) : '',
        }
      }
      setOverrides(ovrs)
      setDirty(false)
    } catch { setError('Failed to load worksheet data.') }
    finally  { setLoading(false) }
  }, [year, month])

  useEffect(() => { load() }, [load])

  function setOvr(date: string, field: keyof DayOverride, val: string) {
    setOverrides(prev => ({ ...prev, [date]: { ...prev[date], [field]: val } }))
    setDirty(true)
  }

  function fillFnBFromLast7() {
    if (!data) return
    const last7 = data.days
      .filter(d => d.is_past && (d.actual_dry != null || d.actual_wet != null))
      .slice(-7)
    if (!last7.length) return
    setOverrides(prev => {
      const next = { ...prev }
      const futureDays = data.days.filter(d => !d.is_past)
      futureDays.forEach((day, i) => {
        const src = last7[i % last7.length]
        next[day.date] = {
          ...next[day.date],
          dry_override: String(src.actual_dry ?? src.forecast_dry ?? ''),
          wet_override: String(src.actual_wet ?? src.forecast_wet ?? ''),
        }
      })
      return next
    })
    setDirty(true)
  }

  async function handleSave() {
    if (!canEdit) return
    setSaving(true)
    try {
      const ovrList = Object.entries(overrides).map(([date, o]) => ({
        date,
        pickup_rooms:    o.pickup_rooms    !== '' ? parseInt(o.pickup_rooms)       : null,
        pickup_avg_rate: o.pickup_avg_rate !== '' ? parseFloat(o.pickup_avg_rate)  : null,
        dry_override:    o.dry_override    !== '' ? parseFloat(o.dry_override)     : null,
        wet_override:    o.wet_override    !== '' ? parseFloat(o.wet_override)     : null,
      }))
      await dfSaveWorksheet(year, month, { pickup_avg_rate: parseFloat(sessionRate) || 135, overrides: ovrList })
      setDirty(false)
      await load()
    } catch { setError('Save failed.') }
    finally  { setSaving(false) }
  }

  function computedDay(day: WorksheetDay) {
    const ovr     = overrides[day.date] || {}
    const pickup  = parseInt(ovr.pickup_rooms || '0') || 0
    const rate    = parseFloat(ovr.pickup_avg_rate || '') || parseFloat(sessionRate) || 135
    const total   = day.otb_rooms + pickup
    const occ     = day.available > 0 ? (total / day.available) * 100 : null
    let   accomm  = day.otb_net_rev + pickup * rate
    if (day.is_past && day.actual_accomm != null) accomm = day.actual_accomm
    const dry = ovr.dry_override !== '' ? parseFloat(ovr.dry_override) : day.forecast_dry
    const wet = ovr.wet_override !== '' ? parseFloat(ovr.wet_override) : day.forecast_wet
    return { total, occ, accomm, dry: dry || 0, wet: wet || 0 }
  }

  if (loading) return <div className="df-loading"><div className="df-spinner" /></div>
  if (!data)   return <div className="df-error">{error || 'No data.'}</div>

  const totals = data.days.reduce((acc, day) => {
    const c = computedDay(day)
    const p = parseInt(overrides[day.date]?.pickup_rooms || '0') || 0
    return {
      available: acc.available + day.available,
      otb: acc.otb + day.otb_rooms,
      pickup: acc.pickup + p,
      total: acc.total + c.total,
      accomm: acc.accomm + c.accomm,
      dry: acc.dry + c.dry,
      wet: acc.wet + c.wet,
    }
  }, { available: 0, otb: 0, pickup: 0, total: 0, accomm: 0, dry: 0, wet: 0 })

  return (
    <div className="df-worksheet">
      <div className="df-toolbar">
        <div className="df-toolbar-left">
          <label className="df-rate-label">
            Default Pickup Rate (£)
            <input
              type="number" value={sessionRate} min="0" step="1"
              onChange={e => { setRate(e.target.value); setDirty(true) }}
              className="df-rate-input" disabled={!canEdit}
            />
          </label>
        </div>
        <div className="df-toolbar-right">
          {error && <span className="df-error-inline">{error}</span>}
          <button className="btn-icon df-btn-icon" onClick={load} title="Refresh">
            <RefreshCw size={13} strokeWidth={1.75} />
          </button>
          {canEdit && (
            <button className="btn-icon df-btn-icon" onClick={fillFnBFromLast7} title="Fill F&B from last 7 days">
              <Repeat2 size={13} strokeWidth={1.75} />
            </button>
          )}
          {canEdit && (
            <button className="btn-run df-save-btn" onClick={handleSave} disabled={saving || !dirty}>
              <Save size={13} strokeWidth={1.75} />
              {saving ? 'Saving…' : 'Save'}
            </button>
          )}
        </div>
      </div>

      <div className="df-table-wrap">
        <table className="df-table">
          <thead>
            <tr>
              <th>Day</th><th>Date</th><th>Avail</th><th>OTB</th>
              <th>OTB Rev</th><th>Avg £</th><th title="ML model suggestion">ML±</th>
              <th>Pickup</th><th>Total</th>
              <th>Act Acc</th><th>Act Dry</th><th>Act Wet</th>
              <th>Occ%</th><th>Fcast Acc</th><th>Fcast Dry</th><th>Fcast Wet</th>
            </tr>
          </thead>
          <tbody>
            {data.days.map((day, i) => {
              const n   = data.days.length
              const c   = computedDay(day)
              const ovr = overrides[day.date] || { pickup_rooms: '', pickup_avg_rate: '', dry_override: '', wet_override: '' }
              const rowCls = day.is_today ? 'df-row-today' : day.is_past ? 'df-row-past' : ''
              // tabIndex groups: pickup col → dry col → wet col (vertical tab order)
              const tabPickup = i + 1
              const tabDry    = n + i + 1
              const tabWet    = n * 2 + i + 1
              return (
                <tr key={day.date} className={rowCls}>
                  <td>{DOW_NAMES[day.dow]}</td>
                  <td>{day.date.slice(5)}</td>
                  <td className="num">{day.available || '—'}</td>
                  <td className="num">{day.otb_rooms}</td>
                  <td className="num">{fmtCcy(day.otb_net_rev || null)}</td>
                  <td className="num">{fmtCcy(day.avg_rate_net)}</td>
                  <td className="num df-ml">
                    {day.ml_suggestion != null ? (day.ml_suggestion > 0 ? `+${day.ml_suggestion}` : day.ml_suggestion) : '—'}
                  </td>
                  <td className="df-input-cell">
                    {canEdit && !day.is_past ? (
                      <input type="number" value={ovr.pickup_rooms} min="0" placeholder="0"
                        tabIndex={tabPickup}
                        onChange={e => setOvr(day.date, 'pickup_rooms', e.target.value)}
                        className="df-cell-input" />
                    ) : (
                      <span className="num">{parseInt(ovr.pickup_rooms || '0') || '—'}</span>
                    )}
                  </td>
                  <td className="num df-bold">{c.total}</td>
                  <td className="num">{fmtCcy(day.actual_accomm)}</td>
                  <td className="num">{fmtCcy(day.actual_dry)}</td>
                  <td className="num">{fmtCcy(day.actual_wet)}</td>
                  <td className="num">{c.occ != null ? `${c.occ.toFixed(1)}%` : '—'}</td>
                  <td className="num df-forecast">{fmtCcy(c.accomm)}</td>
                  <td className="df-input-cell">
                    {canEdit ? (
                      <input type="number" value={ovr.dry_override} step="0.01"
                        tabIndex={tabDry}
                        placeholder={fmtNum(day.forecast_dry, 0)}
                        onChange={e => setOvr(day.date, 'dry_override', e.target.value)}
                        className="df-cell-input" />
                    ) : <span className="num">{fmtCcy(c.dry)}</span>}
                  </td>
                  <td className="df-input-cell">
                    {canEdit ? (
                      <input type="number" value={ovr.wet_override} step="0.01"
                        tabIndex={tabWet}
                        placeholder={fmtNum(day.forecast_wet, 0)}
                        onChange={e => setOvr(day.date, 'wet_override', e.target.value)}
                        className="df-cell-input" />
                    ) : <span className="num">{fmtCcy(c.wet)}</span>}
                  </td>
                </tr>
              )
            })}
          </tbody>
          <tfoot>
            <tr className="df-totals">
              <td colSpan={2}>Totals</td>
              <td className="num">{totals.available}</td>
              <td className="num">{totals.otb}</td>
              <td /><td /><td />
              <td className="num">{totals.pickup}</td>
              <td className="num df-bold">{totals.total}</td>
              <td /><td /><td />
              <td className="num">{totals.available > 0 ? `${((totals.total / totals.available) * 100).toFixed(1)}%` : '—'}</td>
              <td className="num df-bold">{fmtCcy(totals.accomm)}</td>
              <td className="num">{fmtCcy(totals.dry)}</td>
              <td className="num">{fmtCcy(totals.wet)}</td>
            </tr>
          </tfoot>
        </table>
      </div>
    </div>
  )
}

// ── Report tab ────────────────────────────────────────────────────────────────

function VarCell({ a, b }: { a: number; b: number }) {
  const v = a - b
  return <td className={v >= 0 ? 'df-pos' : 'df-neg'}>{v >= 0 ? '+' : ''}{fmtCcy(v)}</td>
}

function ReportTab({ year, month }: { year: number; month: number }) {
  const { user } = useAuth()
  const canEdit = can(user, 'edit')

  const [data, setData]             = useState<ForecastReportData | null>(null)
  const [loading, setLoading]       = useState(true)
  const [error, setError]           = useState<string | null>(null)
  const [snapshotLabel, setLabel]   = useState('')
  const [saving, setSaving]         = useState(false)

  const load = useCallback(async () => {
    setLoading(true); setError(null)
    try   { setData(await dfGetReport(year, month)) }
    catch { setError('Failed to load report data.') }
    finally { setLoading(false) }
  }, [year, month])

  useEffect(() => { load() }, [load])

  async function handleSnapshot() {
    if (!data || !canEdit) return
    setSaving(true)
    try {
      await dfSaveSnapshot(year, month, {
        label: snapshotLabel || `${new Date().toLocaleDateString('en-GB')}`,
        forecast_rooms: data.forecast.rooms,
        forecast_accomm: data.forecast.accomm,
        forecast_dry: data.forecast.dry,
        forecast_wet: data.forecast.wet,
        forecast_total: data.forecast.total,
        otb_rooms: data.otb.rooms,
        pickup_rooms: data.pickup.rooms,
        occ_pct: data.forecast.occ_pct,
      })
      setLabel('')
      await load()
    } catch { setError('Snapshot failed.') }
    finally { setSaving(false) }
  }

  async function handleDeleteSnap(snap: ForecastSnapshot) {
    if (!canEdit) return
    try { await dfDeleteSnapshot(year, month, snap.id); await load() }
    catch { setError('Delete failed.') }
  }

  if (loading) return <div className="df-loading"><div className="df-spinner" /></div>
  if (!data)   return <div className="df-error">{error || 'No data.'}</div>

  const { forecast, budget, last_year, otb, pickup, weekly, snapshots } = data

  return (
    <div className="df-report" id="df-printable">
      <div className="df-report-toolbar no-print">
        <button className="btn-icon df-btn-icon" onClick={() => window.print()} title="Print">
          <Printer size={13} strokeWidth={1.75} />
        </button>
        <button className="btn-icon df-btn-icon" onClick={load} title="Refresh">
          <RefreshCw size={13} strokeWidth={1.75} />
        </button>
        {canEdit && (
          <div className="df-snap-row">
            <input type="text" value={snapshotLabel} placeholder="Snapshot label (optional)"
              onChange={e => setLabel(e.target.value)} className="df-snap-input" />
            <button className="btn-run df-save-btn" onClick={handleSnapshot} disabled={saving}>
              <Camera size={13} strokeWidth={1.75} />
              {saving ? 'Saving…' : 'Save Snapshot'}
            </button>
          </div>
        )}
        {error && <span className="df-error-inline">{error}</span>}
      </div>

      <h2 className="df-report-title">
        DIRECTORS FORECAST — {MONTH_NAMES[month - 1].toUpperCase()} {year}
      </h2>

      {/* Forecast vs Budget vs Last Year */}
      <div className="df-card">
        <table className="df-report-table">
          <thead>
            <tr>
              <th></th>
              <th>Forecast</th><th>Budget</th><th>Budget Var</th>
              <th>Last Year</th><th>LY Var</th>
              {snapshots.map(s => (
                <th key={s.id} className="df-snap-col">
                  {s.label || new Date(s.snapshot_at).toLocaleDateString('en-GB')}
                  {canEdit && (
                    <button className="df-del-snap" onClick={() => handleDeleteSnap(s)} title="Delete">
                      <Trash2 size={10} strokeWidth={1.75} />
                    </button>
                  )}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            <tr>
              <td className="df-row-label">Rooms</td>
              <td className="num">{forecast.rooms.toLocaleString()}</td>
              <td className="num">—</td><td className="num">—</td>
              <td className="num">{last_year.rooms.toLocaleString()}</td>
              <td className={forecast.rooms >= last_year.rooms ? 'num df-pos' : 'num df-neg'}>
                {forecast.rooms - last_year.rooms >= 0 ? '+' : ''}{(forecast.rooms - last_year.rooms).toLocaleString()}
              </td>
              {snapshots.map(s => <td key={s.id} className="num df-snap-val">{s.forecast_rooms?.toLocaleString()}</td>)}
            </tr>
            {([['Accommodation', 'accomm'], ['Dry', 'dry'], ['Wet', 'wet']] as const).map(([label, key]) => (
              <tr key={key}>
                <td className="df-row-label">{label}</td>
                <td className="num">{fmtCcy(forecast[key])}</td>
                <td className="num">{fmtCcy(budget[key])}</td>
                <VarCell a={forecast[key]} b={budget[key]} />
                <td className="num">{fmtCcy(last_year[key])}</td>
                <VarCell a={forecast[key]} b={last_year[key]} />
                {snapshots.map(s => (
                  <td key={s.id} className="num df-snap-val">
                    {fmtCcy(s[`forecast_${key}` as keyof ForecastSnapshot] as number)}
                  </td>
                ))}
              </tr>
            ))}
            <tr className="df-total-row">
              <td className="df-row-label">Total</td>
              <td className="num">{fmtCcy(forecast.total)}</td>
              <td className="num">{fmtCcy(budget.total)}</td>
              <VarCell a={forecast.total} b={budget.total} />
              <td className="num">{fmtCcy(last_year.total)}</td>
              <VarCell a={forecast.total} b={last_year.total} />
              {snapshots.map(s => <td key={s.id} className="num df-snap-val">{fmtCcy(s.forecast_total)}</td>)}
            </tr>
          </tbody>
        </table>
      </div>

      {/* OTB Occupancy Summary */}
      <div className="df-card">
        <div className="df-card-title">Occupancy Report</div>
        <table className="df-report-table">
          <thead>
            <tr>
              <th></th><th>OTB (Actuals)</th><th>Probable Pickup</th><th>Estimate for Month</th>
            </tr>
          </thead>
          <tbody>
            <tr><td className="df-row-label">Room Nights</td>
              <td className="num">{otb.rooms.toLocaleString()}</td>
              <td className="num">{pickup.rooms.toLocaleString()}</td>
              <td className="num df-bold">{forecast.rooms.toLocaleString()}</td></tr>
            <tr><td className="df-row-label">Revenue</td>
              <td className="num">{fmtCcy(otb.accomm)}</td>
              <td className="num">{fmtCcy(pickup.accomm)}</td>
              <td className="num df-bold">{fmtCcy(forecast.accomm)}</td></tr>
            <tr><td className="df-row-label">ARR</td>
              <td className="num">{fmtCcy(otb.arr)}</td>
              <td className="num">{fmtCcy(pickup.avg_rate)}</td>
              <td className="num df-bold">{fmtCcy(forecast.arr)}</td></tr>
            <tr><td className="df-row-label">Occupancy %</td>
              <td className="num">{fmtPct(otb.occ_pct)}</td>
              <td className="num">{pickup.rooms > 0 && forecast.available > 0 ? fmtPct((pickup.rooms / forecast.available) * 100) : '—'}</td>
              <td className="num df-bold">{fmtPct(forecast.occ_pct)}</td></tr>
            <tr><td className="df-row-label">REVPAR</td>
              <td className="num">{fmtCcy(otb.revpar)}</td>
              <td className="num">—</td>
              <td className="num df-bold">{fmtCcy(forecast.revpar)}</td></tr>
            <tr><td className="df-row-label">Available Rooms</td>
              <td className="num">{forecast.available.toLocaleString()}</td>
              <td className="num">{(forecast.available - otb.rooms).toLocaleString()}</td>
              <td className="num">{forecast.available.toLocaleString()}</td></tr>
          </tbody>
        </table>
      </div>

      {/* Weekly Breakdown */}
      <div className="df-weekly-grid">
        {(['accomm', 'dry', 'wet'] as const).map(cat => (
          <div key={cat} className="df-card">
            <div className="df-card-title">{cat === 'accomm' ? 'Accommodation' : cat === 'dry' ? 'Dry (Food)' : 'Wet (Beverage)'}</div>
            <table className="df-report-table">
              <tbody>
                {weekly.map(w => (
                  <tr key={w.label}>
                    <td className="df-row-label">{w.label}</td>
                    <td className="num">{fmtCcy(w[cat])}</td>
                  </tr>
                ))}
                <tr className="df-total-row">
                  <td className="df-row-label">Total</td>
                  <td className="num">{fmtCcy(weekly.reduce((s, w) => s + w[cat], 0))}</td>
                </tr>
              </tbody>
            </table>
          </div>
        ))}
      </div>
    </div>
  )
}

// ── Main component ────────────────────────────────────────────────────────────

type Tab = 'worksheet' | 'report'

export default function DirectorsForecast() {
  useFrameViewport('desktop')
  const now   = new Date()
  const [year, setYear]   = useState(now.getFullYear())
  const [month, setMonth] = useState(now.getMonth() + 1)
  const [tab, setTab]     = useState<Tab>('worksheet')

  return (
    <div className="df-root">
      <div className="df-header">
        <MonthNav year={year} month={month} onChange={(y, m) => { setYear(y); setMonth(m) }} />
        <div className="df-tabs">
          <button className={`df-tab${tab === 'worksheet' ? ' active' : ''}`} onClick={() => setTab('worksheet')}>
            Worksheet
          </button>
          <button className={`df-tab${tab === 'report' ? ' active' : ''}`} onClick={() => setTab('report')}>
            Report
          </button>
        </div>
      </div>

      {tab === 'worksheet'
        ? <WorksheetTab key={`ws-${year}-${month}`} year={year} month={month} />
        : <ReportTab    key={`rp-${year}-${month}`} year={year} month={month} />}
    </div>
  )
}
