<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue';
import Button from 'primevue/button';
import DatePicker from 'primevue/datepicker';
import Tabs from 'primevue/tabs';
import TabList from 'primevue/tablist';
import Tab from 'primevue/tab';
import TabPanels from 'primevue/tabpanels';
import TabPanel from 'primevue/tabpanel';
import Card from 'primevue/card';
import Message from 'primevue/message';
import Tag from 'primevue/tag';
import ProgressSpinner from 'primevue/progressspinner';
import { useToast } from 'primevue/usetoast';
import BasePageHeader from '@/core/components/BasePageHeader.vue';
import TimesheetEntryRow from '../components/TimesheetEntryRow.vue';
import { timesheetService } from '../timesheet.service';
import { projectService } from '@/modules/project/project.service';
import { TIMESHEET_RULES, TIMESHEET_STATUS, ENTRY_TYPES } from '@/core/constants/app.constants';
import type { Timesheet, TimesheetEntry, EntryType } from '../timesheet.types';

const toast = useToast();

const loading = ref(false);
const saving = ref(false);
const submitting = ref(false);
const activeTab = ref<string>('NON_PROJECT');

// Week navigation
const weekStart = ref<Date>(getMonday(new Date()));

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;
}

const weekEnd = computed(() => addDays(weekStart.value, 5));
const weekLabel = computed(() => `${formatDate(weekStart.value)} ~ ${formatDate(weekEnd.value)}`);

const days = computed(() => {
  return Array.from({ length: 6 }, (_, i) => formatDate(addDays(weekStart.value, i)));
});
const dayLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

function prevWeek() {
  weekStart.value = addDays(weekStart.value, -7);
}
function nextWeek() {
  weekStart.value = addDays(weekStart.value, 7);
}

// Data
const timesheet = ref<Timesheet | null>(null);
const projects = ref<any[]>([]);
const wbsList = ref<any[]>([]);
const tealList = ref<any[]>([]);

// Entry rows grouped by type
interface EntryRow {
  _uid: number;
  id?: number;
  entryType: EntryType;
  npCategory?: string;
  otherProjectId?: number;
  epcProjectId?: number;
  canonicalWbsId?: number;
  tealEntryId?: number;
  hours: Record<string, number>;
  remark?: string;
}

let uidCounter = 0;
const entryRows = ref<EntryRow[]>([]);

const npRows = computed(() => entryRows.value.filter((r) => r.entryType === 'NON_PROJECT'));
const otherRows = computed(() => entryRows.value.filter((r) => r.entryType === 'OTHER_PROJECT'));
const epcRows = computed(() => entryRows.value.filter((r) => r.entryType === 'EPC'));

function rowsForTab(tab: string) {
  if (tab === 'NON_PROJECT') return npRows.value;
  if (tab === 'OTHER_PROJECT') return otherRows.value;
  return epcRows.value;
}

// Totals
const totalHours = computed(() => {
  return entryRows.value.reduce((sum, row) => {
    return sum + Object.values(row.hours).reduce((a, b) => a + b, 0);
  }, 0);
});

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

// Warnings
const warnings = computed(() => {
  const msgs: string[] = [];
  for (const [date, total] of Object.entries(dailyTotals.value)) {
    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.value > TIMESHEET_RULES.maxWeeklyHours) {
    msgs.push(`주간 합계 ${totalHours.value}시간 - 최대 ${TIMESHEET_RULES.maxWeeklyHours}h 초과!`);
  }
  return msgs;
});

// Convert server entries to rows
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,
      });
    }
    const row = grouped.get(key)!;
    row.hours[e.entryDate] = e.hours;
  }
  return Array.from(grouped.values());
}

// Convert rows back to entries for saving
function rowsToEntries(): any[] {
  const entries: any[] = [];
  for (const row of entryRows.value) {
    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) {
  entryRows.value.push({
    _uid: ++uidCounter,
    entryType: type,
    hours: {},
  });
}

function removeRow(uid: number) {
  entryRows.value = entryRows.value.filter((r) => r._uid !== uid);
}

function updateRow(uid: number, updated: any) {
  const idx = entryRows.value.findIndex((r) => r._uid === uid);
  if (idx >= 0) {
    entryRows.value[idx] = { ...entryRows.value[idx], ...updated };
  }
}

// Load
async function loadWeek() {
  loading.value = true;
  try {
    const { data } = await timesheetService.getWeekly(formatDate(weekStart.value));
    timesheet.value = data as Timesheet;
    entryRows.value = entriesToRows((data as Timesheet).entries ?? []);
  } catch {
    timesheet.value = null;
    entryRows.value = [];
  } finally {
    loading.value = false;
  }
}

async function loadProjects() {
  try {
    const { data } = await projectService.getMy();
    projects.value = (data as any).items ?? data ?? [];
  } catch {
    projects.value = [];
  }
}

// Save
async function saveDraft() {
  if (!timesheet.value) return;
  saving.value = true;
  try {
    const entries = rowsToEntries();
    await timesheetService.saveBatch(timesheet.value.id, entries);
    toast.add({ severity: 'success', summary: '저장', detail: '임시 저장되었습니다.', life: 3000 });
    await loadWeek();
  } catch {
    toast.add({ severity: 'error', summary: '오류', detail: '저장 실패', life: 5000 });
  } finally {
    saving.value = false;
  }
}

async function submitTimesheet() {
  if (!timesheet.value) return;
  if (warnings.value.some((w) => w.includes('초과!'))) {
    toast.add({ severity: 'error', summary: '오류', detail: '규칙 위반 항목이 있습니다. 수정 후 제출해주세요.', life: 5000 });
    return;
  }
  submitting.value = true;
  try {
    const entries = rowsToEntries();
    await timesheetService.saveBatch(timesheet.value.id, entries);
    await timesheetService.submit(timesheet.value.id);
    toast.add({ severity: 'success', summary: '제출', detail: '시수가 제출되었습니다. (결재 요청)', life: 3000 });
    await loadWeek();
  } catch {
    toast.add({ severity: 'error', summary: '오류', detail: '제출 실패', life: 5000 });
  } finally {
    submitting.value = false;
  }
}

const isEditable = computed(() => {
  if (!timesheet.value) return true;
  return timesheet.value.status === 'DRAFT' || timesheet.value.status === 'REJECTED';
});

const statusInfo = computed(() => {
  if (!timesheet.value) return null;
  return (TIMESHEET_STATUS as Record<string, any>)[timesheet.value.status];
});

watch(weekStart, () => loadWeek());

onMounted(() => {
  loadWeek();
  loadProjects();
});
</script>

<template>
  <div class="timesheet-week-view">
    <BasePageHeader title="시수 입력" :subtitle="weekLabel">
      <template #actions>
        <Tag v-if="statusInfo" :value="statusInfo.label" :severity="statusInfo.severity" />
      </template>
    </BasePageHeader>

    <!-- Week Picker -->
    <div class="timesheet-week-view__week-picker">
      <Button icon="pi pi-chevron-left" text rounded @click="prevWeek" />
      <DatePicker
        v-model="weekStart"
        dateFormat="yy-mm-dd"
        :firstDayOfWeek="1"
        style="width: 160px"
      />
      <Button icon="pi pi-chevron-right" text rounded @click="nextWeek" />
    </div>

    <div v-if="loading" style="display: flex; justify-content: center; padding: 3rem;">
      <ProgressSpinner />
    </div>

    <template v-else>
      <!-- Tabs -->
      <Tabs v-model:value="activeTab">
        <TabList>
          <Tab value="NON_PROJECT">{{ ENTRY_TYPES.NON_PROJECT.label }}</Tab>
          <Tab value="OTHER_PROJECT">{{ ENTRY_TYPES.OTHER_PROJECT.label }}</Tab>
          <Tab value="EPC">{{ ENTRY_TYPES.EPC.label }}</Tab>
        </TabList>
        <TabPanels>
          <TabPanel v-for="tabKey in (['NON_PROJECT', 'OTHER_PROJECT', 'EPC'] as EntryType[])" :key="tabKey" :value="tabKey">
            <div class="timesheet-week-view__table-wrapper">
              <table class="timesheet-week-view__table">
                <thead>
                  <tr>
                    <th style="min-width: 200px">{{ tabKey === 'NON_PROJECT' ? '카테고리' : tabKey === 'OTHER_PROJECT' ? '프로젝트' : '프로젝트 / WBS / TEAL' }}</th>
                    <th v-for="(label, i) in dayLabels" :key="i" style="width: 80px; text-align: center">
                      {{ label }}<br />
                      <small>{{ days[i]?.slice(5) }}</small>
                    </th>
                    <th style="width: 70px; text-align: center">합계</th>
                    <th style="width: 50px"></th>
                  </tr>
                </thead>
                <tbody>
                  <TimesheetEntryRow
                    v-for="row in rowsForTab(tabKey)"
                    :key="row._uid"
                    :entry="row"
                    :projects="projects"
                    :wbsList="wbsList"
                    :tealList="tealList"
                    :days="days"
                    :dayLabels="dayLabels"
                    @update:entry="updateRow(row._uid, $event)"
                    @remove="removeRow(row._uid)"
                  />
                  <tr v-if="rowsForTab(tabKey).length === 0">
                    <td :colspan="dayLabels.length + 3" style="text-align: center; padding: 1.5rem; color: var(--p-text-muted-color);">
                      항목이 없습니다. 아래 버튼으로 추가하세요.
                    </td>
                  </tr>
                </tbody>
                <tfoot>
                  <tr>
                    <td><strong>소계</strong></td>
                    <td v-for="day in days" :key="day" style="text-align: center;">
                      {{ dailyTotals[day]?.toFixed(1) ?? '0.0' }}
                    </td>
                    <td style="text-align: center;"><strong>{{ totalHours.toFixed(1) }}h</strong></td>
                    <td></td>
                  </tr>
                </tfoot>
              </table>
            </div>

            <div style="margin-top: 0.5rem;">
              <Button
                :label="'+ 행 추가'"
                text
                size="small"
                :disabled="!isEditable"
                @click="addRow(tabKey)"
              />
            </div>
          </TabPanel>
        </TabPanels>
      </Tabs>

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

      <!-- Warnings -->
      <div v-if="warnings.length" class="timesheet-week-view__warnings">
        <Message v-for="(w, i) in warnings" :key="i" :severity="w.includes('초과!') ? 'error' : 'warn'" :closable="false">
          {{ w }}
        </Message>
      </div>
    </template>
  </div>
</template>

<style lang="scss" scoped>
@use '@/assets/styles/variables' as *;

.timesheet-week-view {
  &__week-picker {
    display: flex;
    align-items: center;
    gap: $space-sm;
    margin-bottom: $space-md;
  }

  &__table-wrapper {
    overflow-x: auto;
    margin-top: $space-sm;
  }

  &__table {
    width: 100%;
    border-collapse: collapse;

    th, td {
      border: 1px solid $color-border;
      padding: $space-xs $space-sm;
      font-size: $font-size-sm;
    }

    thead th {
      background: var(--p-surface-100);
      font-weight: 600;
    }

    tfoot td {
      background: var(--p-surface-50);
      font-weight: 600;
    }
  }

  &__summary {
    margin-top: $space-md;
  }

  &__summary-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: $space-md;
  }

  &__summary-actions {
    display: flex;
    gap: $space-sm;
  }

  &__warnings {
    display: flex;
    flex-direction: column;
    gap: $space-xs;
    margin-top: $space-md;
  }
}
</style>
