'use client';
import { useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import type { Timesheet, TimePunch, PunchInput } from '@alqove/api-client';
import { api } from '@/lib/api';
import { captureCommandGeneration, requireCommandGeneration, requireSelectedStore, useSelectedMembership } from '@/lib/stores/store-context';
import { useTimesheets, useTimesheet, useTimesheetPunches, useTimekeepingGuard, timekeepingError } from '@/hooks/use-timekeeping';
import { addDays, instantToLocal, localToInstant } from '../schedule/schedule-time';

const button = 'rounded border px-4 py-3 disabled:opacity-50';
export function TimesheetsClient() {
  const member = useSelectedMembership();
  if (!member?.capabilities.includes('timesheets.approve')) return <p>You do not have access to timesheets for this store.</p>;
  return <StoreTimesheets key={member.store_id} store={member.store_id} canCorrect={member.capabilities.includes('punches.manage')} />;
}
function StoreTimesheets({ store, canCorrect }: { store: string; canCorrect: boolean }) {
  const guard = useTimekeepingGuard(store, 'timesheets.approve');
  const [week, setWeek] = useState(() => new Date().toLocaleDateString('en-CA'));
  const [selected, setSelected] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<unknown>(null);
  const query = useTimesheets(store, week);
  const qc = useQueryClient();
  async function recalculate() {
    if (busy || !guard.current()) return;
    const generation = captureCommandGeneration();
    setBusy(true); setError(null);
    try { await api.timekeeping.timesheets.recalculate(store, week); requireCommandGeneration(generation); }
    catch (e) { if (guard.current()) setError(e); }
    finally { if (guard.current()) { await qc.invalidateQueries({ queryKey: ['timekeeping', store] }); if (guard.current()) setBusy(false); } }
  }
  return <section className="space-y-5 p-4"><h1 className="text-2xl font-semibold">Timesheets</h1>
    <div className="flex flex-wrap items-end gap-3"><label>Week containing <input type="date" className={button} value={week} onChange={e => { setWeek(e.target.value); setSelected(null); }} /></label><button className={button} disabled={busy || !week} onClick={recalculate}>Recalculate week</button></div>
    {(error != null || query.error) && <p role="alert">{timekeepingError(error ?? query.error)}</p>}
    {query.isPending && <p role="status">Loading timesheets…</p>}
    {query.data?.data.length === 0 && <p>No timesheets. Recalculate this week to build them from punches.</p>}
    <div className="overflow-x-auto"><table className="w-full text-left"><caption className="sr-only">Weekly staff hours</caption><thead><tr>{['Employee', 'Week', 'Scheduled min', 'Regular min', 'Overtime min', 'Double time min', 'Status'].map(h => <th key={h} className="p-3">{h}</th>)}</tr></thead><tbody>{query.data?.data.map(t => <tr key={t.id} className="border-t"><td className="p-3"><button className="underline" onClick={() => setSelected(t.id)}>{t.member_name || 'Staff member'}</button></td><td>{t.week_start_date}</td><td>{t.scheduled_minutes}</td><td>{t.regular_minutes}</td><td>{t.overtime_minutes}</td><td>{t.doubletime_minutes}</td><td>{t.status}</td></tr>)}</tbody></table></div>
    {selected && <TimesheetDetail key={selected} store={store} id={selected} canCorrect={canCorrect} close={() => setSelected(null)} />}
  </section>;
}
function TimesheetDetail({ store, id, canCorrect, close }: { store: string; id: string; canCorrect: boolean; close: () => void }) {
  const guard = useTimekeepingGuard(store, 'timesheets.approve');
  const query = useTimesheet(store, id);
  const qc = useQueryClient();
  const [editing, setEditing] = useState<TimePunch | 'new' | null>(null);
  const [deleting, setDeleting] = useState<TimePunch | null>(null);
  const [note, setNote] = useState('');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<unknown>(null);
  const [acknowledge, setAcknowledge] = useState(false);
  const sheet = query.data?.data;
  const [fromOverride, setFrom] = useState('');
  const [toOverride, setTo] = useState('');
  const from = fromOverride || (sheet ? addDays(sheet.week_start_date, -1) : '');
  const to = toOverride || (sheet ? addDays(sheet.week_start_date, 7) : '');
  const punches = useTimesheetPunches(store, sheet?.store_membership_id, from, to, canCorrect);
  async function refresh() { await qc.invalidateQueries({ queryKey: ['timekeeping', store] }); }
  async function mutate(action: () => Promise<unknown>) {
    if (busy || !guard.current()) return;
    const generation = captureCommandGeneration();
    setBusy(true); setError(null);
    try { await action(); requireCommandGeneration(generation); if (guard.current()) { setEditing(null); setDeleting(null); setNote(''); setAcknowledge(false); } }
    catch (e) { if (guard.current()) setError(e); }
    finally { if (guard.current()) { await refresh(); if (guard.current()) setBusy(false); } }
  }
  async function correct(action: () => Promise<unknown>) {
    return mutate(() => {
      requireSelectedStore(store, 'punches.manage');
      // Read the authoritative cache at dispatch, not the last rendered sheet.
      const latest = qc.getQueryData<{ data: Timesheet }>(['timekeeping', store, 'timesheet', id]);
      if (latest?.data.status !== 'pending') throw new Error('Timesheet is no longer pending. Refresh before correcting punches.');
      return action();
    });
  }
  const metadata = sheet?.calculation_metadata;
  const stranded = !!metadata?.stranded_clock_ins?.length || !!metadata?.open_sessions?.length;
  const oversized = Array.isArray(metadata?.oversized_sessions) && metadata.oversized_sessions.length > 0;
  const timezone = metadata?.timezone ?? 'UTC';
  return <section aria-label="Timesheet detail" className="space-y-4 rounded-xl border p-4"><div className="flex justify-between"><h2 className="text-xl font-semibold">{sheet?.member_name ?? 'Timesheet'} · {sheet?.week_start_date}</h2><button className={button} onClick={close}>Close detail</button></div>
    {query.isPending && <p role="status">Loading detail…</p>}
    {(error != null || query.error) && <p role="alert">{timekeepingError(error ?? query.error)}</p>}
    {sheet && <><p>{sheet.status} · All punch times in {timezone}</p>
      {canCorrect && <div className="flex flex-wrap gap-3"><label>Punches from <input className={button} type="date" value={from} onChange={e => setFrom(e.target.value)} /></label><label>Punches through <input className={button} type="date" value={to} onChange={e => setTo(e.target.value)} /></label><p className="text-sm">Includes boundary days for overnight sessions. Adjust the range to find earlier punches (up to 31 days).</p></div>}
      {punches.error && <p role="alert">{timekeepingError(punches.error)}</p>}
      {canCorrect && punches.isPending && <p role="status">Loading punches…</p>}
      {stranded && <p role="alert">Unclosed clock-in: correct the missing clock-out before approval or export.</p>}
      {oversized && <label className="block"><input type="checkbox" checked={acknowledge} onChange={e => setAcknowledge(e.target.checked)} /> I reviewed the oversized sessions and acknowledge them.</label>}
      <div className="flex flex-wrap gap-3">
        {sheet.status === 'pending' && <button className={button} disabled={busy || stranded || (oversized && !acknowledge)} onClick={() => mutate(() => api.timekeeping.timesheets.approve(store, id, acknowledge))}>Approve timesheet</button>}
        {sheet.status === 'approved' && <button className={button} disabled={busy} onClick={() => mutate(() => api.timekeeping.timesheets.unlock(store, id))}>Unlock timesheet</button>}
        {sheet.status === 'exported' && <p>Exported timesheets are immutable.</p>}
        {canCorrect && sheet.status === 'pending' && <button className={button} disabled={busy} onClick={() => { setEditing('new'); setDeleting(null); }}>Add punch</button>}
      </div>
      <div className="overflow-x-auto"><table className="w-full text-left"><caption className="sr-only">Punch breakdown</caption><thead><tr><th>Type</th><th>Time</th><th>Break</th><th>Note</th><th>Corrections</th></tr></thead><tbody>{punches.data?.data.map(p => <tr key={p.id} className="border-t"><td className="py-3">{p.punch_type.replaceAll('_', ' ')}</td><td>{instantToLocal(p.punched_at, timezone).replace('T', ' ')}</td><td>{p.break_type ?? '—'}</td><td>{p.note ?? '—'}</td><td>{canCorrect && sheet.status === 'pending' && <div className="flex gap-2"><button className={button} disabled={busy} onClick={() => { setEditing(p); setDeleting(null); }}>Edit punch</button><button className={button} disabled={busy} onClick={() => { setDeleting(p); setEditing(null); setNote(''); }}>Delete punch</button></div>}</td></tr>)}</tbody></table></div>
      {canCorrect && sheet.status === 'pending' && editing && <PunchForm key={editing === 'new' ? 'new' : editing.id + editing.revision} sheet={sheet} punch={editing === 'new' ? undefined : editing} timezone={timezone} busy={busy} cancel={() => setEditing(null)} save={input => correct(() => editing === 'new' ? api.timekeeping.punches.create(store, input) : api.timekeeping.punches.update(store, editing.id, { punch_type: input.punch_type, punched_at: input.punched_at, break_type: input.break_type, note: input.note, revision: editing.revision }))} />}
      {canCorrect && sheet.status === 'pending' && deleting && <form className="space-y-3 rounded border p-4" onSubmit={e => { e.preventDefault(); if (note.trim()) void correct(() => api.timekeeping.punches.remove(store, deleting.id, { revision: deleting.revision, note: note.trim() })); }}><p>Delete {deleting.punch_type.replaceAll('_', ' ')}? This correction is audited.</p><label className="block">Deletion note <input required className={button} value={note} onChange={e => setNote(e.target.value)} /></label><button className={button} disabled={busy || !note.trim()}>Confirm deletion</button> <button type="button" className={button} onClick={() => setDeleting(null)}>Cancel deletion</button></form>}
    </>}
  </section>;
}
function PunchForm({ sheet, punch, timezone, busy, save, cancel }: { sheet: Timesheet; punch?: TimePunch; timezone: string; busy: boolean; save: (input: PunchInput) => Promise<void>; cancel: () => void }) {
  const initialTime = punch ? instantToLocal(punch.punched_at, timezone) : `${sheet.week_start_date}T09:00`;
  const [time, setTime] = useState(initialTime);
  const [type, setType] = useState<TimePunch['punch_type']>(punch?.punch_type ?? 'clock_in');
  const [breakType, setBreakType] = useState<'paid' | 'unpaid'>(punch?.break_type ?? 'unpaid');
  const [note, setNote] = useState('');
  const [error, setError] = useState('');
  return <form className="grid gap-3 rounded border p-4 sm:grid-cols-2" onSubmit={e => {
    e.preventDefault(); if (!note.trim()) return;
    try {
      const punched_at = punch && time === initialTime ? punch.punched_at : localToInstant(time, timezone);
      setError(''); void save({ store_membership_id: sheet.store_membership_id, punch_type: type, punched_at, break_type: type === 'break_start' || type === 'break_end' ? breakType : null, note: note.trim() });
    } catch (e) { setError(timekeepingError(e)); }
  }}>
    <label> Punch type <select className={button} value={type} onChange={e => setType(e.target.value as TimePunch['punch_type'])}>{(['clock_in', 'clock_out', 'break_start', 'break_end'] as const).map(t => <option key={t} value={t}>{t.replaceAll('_', ' ')}</option>)}</select></label>
    <label>Punch time ({timezone}) <input required className={button} type="datetime-local" value={time} onChange={e => setTime(e.target.value)} /></label>
    {(type === 'break_start' || type === 'break_end') && <label>Break type <select className={button} value={breakType} onChange={e => setBreakType(e.target.value as 'paid' | 'unpaid')}><option value="unpaid">Unpaid</option><option value="paid">Paid</option></select></label>}
    <label>Correction note <input required className={button} value={note} onChange={e => setNote(e.target.value)} /></label>
    {error && <p role="alert">{error}</p>}
    <div className="flex gap-2"><button className={button} disabled={busy || !note.trim()}>Save correction</button><button className={button} type="button" disabled={busy} onClick={cancel}>Cancel correction</button></div>
  </form>;
}
