import { useState, useEffect, useMemo, useCallback } from 'react';
import { Button } from 'primereact/button';
import { Calendar } from 'primereact/calendar';
import { TabView, TabPanel } from 'primereact/tabview';
import { Card } from 'primereact/card';
import { Message } from 'primereact/message';
import { Tag } from 'primereact/tag';
import { ProgressSpinner } from 'primereact/progressspinner';
import BasePageHeader from '@/core/components/BasePageHeader';
import TimesheetEntryRow from '../components/TimesheetEntryRow';
import { timesheetService } from '../timesheet.service';
import { projectService } from '@/modules/project/project.service';
import { toArray } from '@/core/api/utils';
import { TIMESHEET_RULES, TIMESHEET_STATUS, ENTRY_TYPES } from '@/core/constants/app.constants';
import type { Timesheet, TimesheetEntry, EntryType } from '../timesheet.types';
import './TimesheetWeekView.scss';

interface EntryRow {
  _uid: number;
  entryType: EntryType;
  npCategory?: string;
  otherProjectId?: number;
  epcProjectId?: number;
  canonicalWbsId?: number;
  tealEntryId?: number;
  hours: Record<string, number>;
  remark?: string;
}

let uidCounter = 0;

function getMonday(d: Date): Date {
  const date = new Date(d);
  const day = date.getDay();
  const diff = date.getDate() - day + (day === 0 ? -6 : 1);
  date.setDate(diff);
  date.setHours(0, 0, 0, 0);
  return date;
}
function formatDate(d: Date): string {
  return d.toISOString().slice(0, 10);
}
function addDays(d: Date, n: number): Date {
  const r = new Date(d);
  r.setDate(r.getDate() + n);
  return r;
}

function entriesToRows(entries: TimesheetEntry[]): EntryRow[] {
  const grouped = new Map<string, EntryRow>();
  for (const e of entries) {
    const key = `${e.entryType}-${e.npCategory ?? ''}-${e.otherProjectId ?? ''}-${e.epcProjectId ?? ''}-${e.canonicalWbsId ?? ''}-${e.tealEntryId ?? ''}`;
    if (!grouped.has(key)) {
      grouped.set(key, {
        _uid: ++uidCounter,
        entryType: e.entryType,
        npCategory: e.npCategory,
        otherProjectId: e.otherProjectId,
        epcProjectId: e.epcProjectId,
        canonicalWbsId: e.canonicalWbsId,
        tealEntryId: e.tealEntryId,
        hours: {},
        remark: e.remark,
      });
    }
    grouped.get(key)!.hours[e.entryDate] = e.hours;
  }
  return Array.from(grouped.values());
}

export default function TimesheetWeekView() {
  const [loading, setLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [weekStart, setWeekStart] = useState<Date>(getMonday(new Date()));
  const [timesheet, setTimesheet] = useState<Timesheet | null>(null);
  const [entryRows, setEntryRows] = useState<EntryRow[]>([]);
  const [projects, setProjects] = useState<{ id: number; name: string }[]>([]);

  const weekEnd = useMemo(() => addDays(weekStart, 5), [weekStart]);
  const weekLabel = `${formatDate(weekStart)} ~ ${formatDate(weekEnd)}`;
  const days = useMemo(() => Array.from({ length: 6 }, (_, i) => formatDate(addDays(weekStart, i))), [weekStart]);
  const dayLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

  const totalHours = useMemo(() =>
    entryRows.reduce((sum, row) => sum + Object.values(row.hours).reduce((a, b) => a + b, 0), 0),
    [entryRows],
  );

  const dailyTotals = useMemo(() => {
    const totals: Record<string, number> = {};
    for (const d of days) {
      totals[d] = entryRows.reduce((sum, row) => sum + (row.hours[d] ?? 0), 0);
    }
    return totals;
  }, [entryRows, days]);

  const warnings = useMemo(() => {
    const msgs: string[] = [];
    for (const [date, total] of Object.entries(dailyTotals)) {
      if (total > TIMESHEET_RULES.warnDailyHours) msgs.push(`${date}: 일 ${total}시간 입력 - 기준(${TIMESHEET_RULES.defaultDailyHours}h) 초과`);
      if (total > TIMESHEET_RULES.maxDailyHours) msgs.push(`${date}: 일 최대 ${TIMESHEET_RULES.maxDailyHours}시간 초과!`);
    }
    if (totalHours > TIMESHEET_RULES.maxWeeklyHours) msgs.push(`주간 합계 ${totalHours}시간 - 최대 ${TIMESHEET_RULES.maxWeeklyHours}h 초과!`);
    return msgs;
  }, [dailyTotals, totalHours]);

  const isEditable = !timesheet || timesheet.status === 'DRAFT' || timesheet.status === 'REJECTED';
  const statusInfo = timesheet ? TIMESHEET_STATUS[timesheet.status] : null;

  const loadWeek = useCallback(async () => {
    setLoading(true);
    try {
      const { data } = await timesheetService.getWeekly(formatDate(weekStart));
      const ts = data as Timesheet;
      setTimesheet(ts);
      setEntryRows(entriesToRows(ts.entries ?? []));
    } catch {
      setTimesheet(null);
      setEntryRows([]);
    } finally {
      setLoading(false);
    }
  }, [weekStart]);

  useEffect(() => { loadWeek(); }, [loadWeek]);

  useEffect(() => {
    projectService.getMy()
      .then(({ data }) => setProjects(toArray<{ id: number; name: string }>(data)))
      .catch(() => setProjects([]));
  }, []);

  function rowsToEntries() {
    const entries: unknown[] = [];
    for (const row of entryRows) {
      for (const [date, hours] of Object.entries(row.hours)) {
        if (hours > 0) entries.push({ entryType: row.entryType, entryDate: date, hours, npCategory: row.npCategory, otherProjectId: row.otherProjectId, epcProjectId: row.epcProjectId, canonicalWbsId: row.canonicalWbsId, tealEntryId: row.tealEntryId, remark: row.remark });
      }
    }
    return entries;
  }

  function addRow(type: EntryType) {
    setEntryRows((prev) => [...prev, { _uid: ++uidCounter, entryType: type, hours: {} }]);
  }
  function removeRow(uid: number) {
    setEntryRows((prev) => prev.filter((r) => r._uid !== uid));
  }
  function updateRow(uid: number, updated: Partial<EntryRow>) {
    setEntryRows((prev) => prev.map((r) => r._uid === uid ? { ...r, ...updated } : r));
  }

  async function saveDraft() {
    if (!timesheet) return;
    setSaving(true);
    try {
      await timesheetService.saveBatch(timesheet.id, rowsToEntries());
      await loadWeek();
    } catch { /* handled */ }
    finally { setSaving(false); }
  }

  async function submitTimesheet() {
    if (!timesheet) return;
    if (warnings.some((w) => w.includes('초과!'))) return;
    setSubmitting(true);
    try {
      await timesheetService.saveBatch(timesheet.id, rowsToEntries());
      await timesheetService.submit(timesheet.id);
      await loadWeek();
    } catch { /* handled */ }
    finally { setSubmitting(false); }
  }

  const tabTypes: EntryType[] = ['NON_PROJECT', 'OTHER_PROJECT', 'EPC'];

  return (
    <div className="timesheet-week-view">
      <BasePageHeader title="시수 입력" subtitle={weekLabel}
        actions={statusInfo ? <Tag value={statusInfo.label} severity={statusInfo.severity as 'success' | 'info' | 'warning' | 'danger' | 'secondary'} /> : undefined}
      />

      <div className="timesheet-week-view__week-picker">
        <Button icon="pi pi-chevron-left" text rounded onClick={() => setWeekStart(addDays(weekStart, -7))} />
        <Calendar value={weekStart} onChange={(e) => e.value && setWeekStart(getMonday(e.value as Date))} dateFormat="yy-mm-dd" style={{ width: '160px' }} />
        <Button icon="pi pi-chevron-right" text rounded onClick={() => setWeekStart(addDays(weekStart, 7))} />
      </div>

      {loading ? (
        <div style={{ display: 'flex', justifyContent: 'center', padding: '3rem' }}><ProgressSpinner /></div>
      ) : (
        <>
          <TabView>
            {tabTypes.map((tabKey) => (
              <TabPanel key={tabKey} header={ENTRY_TYPES[tabKey]?.label ?? tabKey}>
                <div className="timesheet-week-view__table-wrapper">
                  <table className="timesheet-week-view__table">
                    <thead>
                      <tr>
                        <th style={{ minWidth: 200 }}>{tabKey === 'NON_PROJECT' ? '카테고리' : tabKey === 'OTHER_PROJECT' ? '프로젝트' : '프로젝트 / WBS / TEAL'}</th>
                        {dayLabels.map((label, i) => (
                          <th key={label} style={{ width: 80, textAlign: 'center' }}>{label}<br /><small>{days[i]?.slice(5)}</small></th>
                        ))}
                        <th style={{ width: 70, textAlign: 'center' }}>합계</th>
                        <th style={{ width: 50 }}></th>
                      </tr>
                    </thead>
                    <tbody>
                      {entryRows.filter((r) => r.entryType === tabKey).map((row) => (
                        <TimesheetEntryRow key={row._uid} entry={row} projects={projects} days={days} dayLabels={dayLabels} disabled={!isEditable}
                          onUpdate={(u) => updateRow(row._uid, u)} onRemove={() => removeRow(row._uid)} />
                      ))}
                      {entryRows.filter((r) => r.entryType === tabKey).length === 0 && (
                        <tr><td colSpan={dayLabels.length + 3} style={{ textAlign: 'center', padding: '1.5rem', color: 'var(--p-text-muted-color)' }}>항목이 없습니다. 아래 버튼으로 추가하세요.</td></tr>
                      )}
                    </tbody>
                    <tfoot>
                      <tr>
                        <td><strong>소계</strong></td>
                        {days.map((day) => (<td key={day} style={{ textAlign: 'center' }}>{dailyTotals[day]?.toFixed(1) ?? '0.0'}</td>))}
                        <td style={{ textAlign: 'center' }}><strong>{totalHours.toFixed(1)}h</strong></td>
                        <td></td>
                      </tr>
                    </tfoot>
                  </table>
                </div>
                <div style={{ marginTop: '0.5rem' }}>
                  <Button label="+ 행 추가" text size="small" disabled={!isEditable} onClick={() => addRow(tabKey)} />
                </div>
              </TabPanel>
            ))}
          </TabView>

          <Card className="timesheet-week-view__summary">
            <div className="timesheet-week-view__summary-row">
              <span>주간 합계: <strong>{totalHours.toFixed(1)}</strong> / {TIMESHEET_RULES.maxWeeklyHours}h</span>
              <div className="timesheet-week-view__summary-actions">
                <Button label="임시 저장" severity="secondary" icon="pi pi-save" loading={saving} disabled={!isEditable} onClick={saveDraft} />
                <Button label="제출 (결재 요청)" icon="pi pi-send" loading={submitting} disabled={!isEditable} onClick={submitTimesheet} />
              </div>
            </div>
          </Card>

          {warnings.length > 0 && (
            <div className="timesheet-week-view__warnings">
              {warnings.map((w, i) => <Message key={i} severity={w.includes('초과!') ? 'error' : 'warn'} text={w} />)}
            </div>
          )}
        </>
      )}
    </div>
  );
}
