<script setup lang="ts">
import {
  Download,
  FileSpreadsheet,
  Loader2,
  LockKeyhole,
  Play,
  Upload,
} from "@lucide/vue";
import { computed, onMounted, ref } from "vue";

import { ApiError } from "../api/http";
import {
  calculateMonthlyFromDingTalk,
  calculateMonthlyFromExcel,
  exportMonthlyRun,
  getMonthlyRun,
  listMonthlyRuns,
  lockMonthlyRun,
} from "../api/monthlyPayroll";
import type { MonthlyPayrollDetailResponse, MonthlyPayrollRun } from "../api/types";
import DatePicker from "../components/DatePicker.vue";
import ResultsTable from "../components/ResultsTable.vue";
import { useAuthStore } from "../stores/auth";
import {
  extractMonthFromExcelFilename,
  formatMonthRange,
  isSalaryMonth,
  type ExcelMonthInfo,
} from "../utils/payrollMonth";

const auth = useAuthStore();
const selectedMonth = ref(defaultSalaryMonth());
const selectedFile = ref<File | null>(null);
const selectedFileMonth = ref<ExcelMonthInfo | null>(null);
const exportExcel = ref(true);
const runs = ref<MonthlyPayrollRun[]>([]);
const detail = ref<MonthlyPayrollDetailResponse | null>(null);
const fileInput = ref<HTMLInputElement | null>(null);
const loading = ref(false);
const actionLoading = ref<"" | "excel" | "dingtalk" | "lock" | "export">("");
const errorMessage = ref("");
const successMessage = ref("");

const activeRun = computed(() => detail.value?.run || runs.value[0] || null);
const isLocked = computed(() => activeRun.value?.status === "locked");
const canCalculate = computed(() => auth.hasPermission("monthly_payroll:calculate"));
const canLock = computed(() => auth.hasPermission("monthly_payroll:lock"));
const canExport = computed(() => auth.hasPermission("monthly_payroll:export"));
const grossTotal = computed(() => money(activeRun.value?.gross_total || 0));
const netTotal = computed(() => money(activeRun.value?.net_total || 0));
const deductionTotal = computed(() => money(activeRun.value?.deduction_total || 0));
const overtimeTotal = computed(() => `${(activeRun.value?.overtime_total || 0).toFixed(1)}h`);
const monthRange = computed(() => formatMonthRange(selectedMonth.value));
const hasValidMonth = computed(() => isSalaryMonth(selectedMonth.value));
const dingtalkScopeText = computed(() =>
  hasValidMonth.value ? `将同步 ${monthRange.value} 的钉钉考勤` : "请选择核算月份后再同步钉钉",
);
const excelMonthMismatch = computed(
  () => Boolean(selectedFile.value && selectedFileMonth.value && selectedFileMonth.value.month !== selectedMonth.value),
);
const excelMonthHint = computed(() => {
  if (!selectedFile.value) {
    return "请先选择 Excel 文件。";
  }
  if (!selectedFileMonth.value) {
    return "未从文件名识别月份，请确认核算月份与 Excel 内容一致。";
  }
  if (excelMonthMismatch.value) {
    return `Excel 文件月份为 ${selectedFileMonth.value.month}，当前核算月份为 ${selectedMonth.value || "未选择"}，请保持一致后再导入。`;
  }
  return `Excel 文件月份：${selectedFileMonth.value.month}（${selectedFileMonth.value.rangeLabel}）`;
});

onMounted(() => {
  void loadRuns();
});

function defaultSalaryMonth(): string {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, "0");
  return `${year}-${month}`;
}

async function loadRuns(preferredRunId?: number): Promise<void> {
  loading.value = true;
  errorMessage.value = "";
  try {
    runs.value = await listMonthlyRuns(selectedMonth.value);
    if (runs.value.length) {
      const selectedRun = runs.value.find((run) => run.id === preferredRunId) || runs.value[0];
      detail.value = await getMonthlyRun(selectedRun.id);
    } else {
      detail.value = null;
    }
  } catch (error) {
    runs.value = [];
    detail.value = null;
    errorMessage.value = error instanceof ApiError ? error.message : "月度核算数据加载失败";
  } finally {
    loading.value = false;
  }
}

async function handleMonthChange(): Promise<void> {
  errorMessage.value = "";
  successMessage.value = "";
  await loadRuns();
}

async function viewRun(run: MonthlyPayrollRun): Promise<void> {
  loading.value = true;
  errorMessage.value = "";
  try {
    detail.value = await getMonthlyRun(run.id);
  } catch (error) {
    errorMessage.value = error instanceof ApiError ? error.message : "核算记录加载失败";
  } finally {
    loading.value = false;
  }
}

function chooseExcel(): void {
  fileInput.value?.click();
}

async function handleExcelChange(event: Event): Promise<void> {
  const input = event.target as HTMLInputElement;
  const file = input.files?.[0];
  input.value = "";
  if (!file) {
    return;
  }

  selectedFile.value = file;
  selectedFileMonth.value = extractMonthFromExcelFilename(file.name);

  let detectedMonthMessage = "";
  if (selectedFileMonth.value && selectedMonth.value !== selectedFileMonth.value.month) {
    selectedMonth.value = selectedFileMonth.value.month;
    detectedMonthMessage = `已根据文件识别核算月份：${selectedFileMonth.value.month}。`;
  }

  await importSelectedExcel(detectedMonthMessage);
}

async function calculateFromDingTalk(): Promise<void> {
  if (!hasValidMonth.value) {
    errorMessage.value = "请选择核算月份";
    return;
  }

  await runAction("dingtalk", async () => {
    const calculated = await calculateMonthlyFromDingTalk(selectedMonth.value, exportExcel.value);
    detail.value = calculated;
    successMessage.value = `钉钉考勤已同步并完成 ${calculated.run.salary_month} 工资核算`;
    await loadRuns(calculated.run.id);
  });
}

async function importSelectedExcel(prefixMessage = ""): Promise<void> {
  if (!selectedFile.value) {
    errorMessage.value = "请选择钉钉月度汇总 Excel 文件";
    return;
  }
  if (!hasValidMonth.value) {
    errorMessage.value = "请选择核算月份";
    return;
  }
  if (excelMonthMismatch.value) {
    errorMessage.value = excelMonthHint.value;
    return;
  }

  await runAction("excel", async () => {
    const calculated = await calculateMonthlyFromExcel(selectedFile.value!, selectedMonth.value, exportExcel.value);
    detail.value = calculated;
    successMessage.value = `${prefixMessage}Excel 已导入并完成 ${calculated.run.salary_month} 工资核算`;
    await loadRuns(calculated.run.id);
  });
}

async function lockRun(): Promise<void> {
  if (!activeRun.value || !window.confirm("锁定后本月工资不能重新计算，只能导出。确认锁定？")) {
    return;
  }
  const runId = activeRun.value.id;
  await runAction("lock", async () => {
    await lockMonthlyRun(runId);
    detail.value = await getMonthlyRun(runId);
    successMessage.value = "本月工资已锁定";
    await loadRuns(runId);
  });
}

async function exportRun(): Promise<void> {
  if (!activeRun.value) {
    return;
  }
  await runAction("export", async () => {
    await exportMonthlyRun(activeRun.value!.id);
    successMessage.value = "月度工资表已开始下载";
  });
}

async function runAction(
  action: "excel" | "dingtalk" | "lock" | "export",
  executor: () => Promise<void>,
): Promise<void> {
  actionLoading.value = action;
  errorMessage.value = "";
  successMessage.value = "";
  try {
    await executor();
  } catch (error) {
    errorMessage.value = error instanceof ApiError ? error.message : "操作失败，请检查后重试";
  } finally {
    actionLoading.value = "";
  }
}

function statusText(status: string | undefined): string {
  const map: Record<string, string> = {
    draft: "草稿",
    calculated: "已计算",
    reviewed: "已复核",
    locked: "已锁定",
    failed: "失败",
  };
  return status ? map[status] || status : "未开始";
}

function sourceText(sourceType: string | undefined): string {
  const map: Record<string, string> = {
    excel: "Excel导入",
    dingtalk: "钉钉同步",
    manual: "手动重算",
  };
  return sourceType ? map[sourceType] || sourceType : "-";
}

function exceptionTypeText(type: string | undefined): string {
  const map: Record<string, string> = {
    missing_employee: "未匹配员工",
    missing_salary_profile: "缺少薪资档案",
    negative_remaining_overtime: "剩余加班不足",
    late_penalty: "迟到扣款",
    missing_card: "缺卡",
    missing_attendance: "缺少考勤",
    salary_calculation_failed: "工资计算失败",
  };
  return type ? map[type] || type : "-";
}

function severityText(severity: string | undefined): string {
  const map: Record<string, string> = {
    info: "提醒",
    warning: "预警",
    error: "错误",
  };
  return severity ? map[severity] || severity : "-";
}

function exceptionStatusText(status: string | undefined): string {
  const map: Record<string, string> = {
    pending: "待处理",
    resolved: "已处理",
    ignored: "已忽略",
  };
  return status ? map[status] || status : "-";
}

function dateTime(value: string | null | undefined): string {
  return value ? new Date(value).toLocaleString() : "-";
}

function money(value: number): string {
  return new Intl.NumberFormat("zh-CN", {
    style: "currency",
    currency: "CNY",
    maximumFractionDigits: 2,
  }).format(value);
}
</script>

<template>
  <div class="view-stack">
    <header class="page-header">
      <div>
        <h1>工资核算</h1>
        <p>一个入口完成考勤取数、工资计算、异常核对、锁定和导出。</p>
      </div>
    </header>

    <p v-if="errorMessage" class="form-error">{{ errorMessage }}</p>
    <p v-if="successMessage" class="form-success">{{ successMessage }}</p>

    <section class="panel payroll-compact-toolbar" aria-label="工资核算工具栏">
      <div class="payroll-toolbar-month">
        <span>本月核算</span>
        <label class="field">
          <span>核算月份</span>
          <DatePicker v-model="selectedMonth" mode="month" placeholder="请选择核算月份" @change="handleMonthChange" />
        </label>
      </div>

      <div class="payroll-toolbar-summary">
        <div>
          <span>实发合计</span>
          <strong>{{ netTotal }}</strong>
          <small>{{ sourceText(activeRun?.source_type) }} · {{ activeRun?.employee_count || 0 }} 人</small>
        </div>
        <div>
          <span>应发合计</span>
          <strong>{{ grossTotal }}</strong>
          <small>{{ statusText(activeRun?.status) }}</small>
        </div>
        <div>
          <span>扣款合计</span>
          <strong>{{ deductionTotal }}</strong>
          <small>加班 {{ overtimeTotal }}</small>
        </div>
        <div class="payroll-toolbar-range">
          <span>同步范围</span>
          <strong>{{ monthRange }}</strong>
          <small>{{ dingtalkScopeText }}</small>
        </div>
      </div>

      <div class="payroll-toolbar-actions">
        <label class="payroll-toolbar-export">
          <input v-model="exportExcel" type="checkbox" />
          <span>生成 Excel 结果文件</span>
        </label>
        <button class="primary-button" type="button" :disabled="!canCalculate || !hasValidMonth || isLocked || actionLoading === 'dingtalk'" @click="calculateFromDingTalk">
          <Loader2 v-if="actionLoading === 'dingtalk'" class="spin" :size="18" aria-hidden="true" />
          <Play v-else :size="18" aria-hidden="true" />
          <span>{{ actionLoading === "dingtalk" ? "同步中" : "同步钉钉核算" }}</span>
        </button>
        <button class="secondary-button" type="button" :disabled="!canCalculate || isLocked || actionLoading === 'excel'" @click="chooseExcel">
          <Loader2 v-if="actionLoading === 'excel'" class="spin" :size="18" aria-hidden="true" />
          <Upload v-else :size="18" aria-hidden="true" />
          <span>{{ selectedFile ? "更换 Excel 核算" : "导入 Excel 核算" }}</span>
        </button>
        <button class="secondary-button" type="button" :disabled="!activeRun || !canLock || isLocked || actionLoading === 'lock'" @click="lockRun">
          <Loader2 v-if="actionLoading === 'lock'" class="spin" :size="18" aria-hidden="true" />
          <LockKeyhole v-else :size="18" aria-hidden="true" />
          <span>锁定工资</span>
        </button>
        <button class="secondary-button" type="button" :disabled="!activeRun || !canExport || actionLoading === 'export'" @click="exportRun">
          <Loader2 v-if="actionLoading === 'export'" class="spin" :size="18" aria-hidden="true" />
          <Download v-else :size="18" aria-hidden="true" />
          <span>导出工资表</span>
        </button>
        <button v-if="selectedFile" class="link-button" type="button" :disabled="!canCalculate || isLocked || actionLoading === 'excel' || excelMonthMismatch" @click="importSelectedExcel()">
          重新导入
        </button>
      </div>
      <input ref="fileInput" accept=".xlsx,.xls" hidden type="file" @change="handleExcelChange" />
    </section>

    <section class="panel payroll-detail-panel">
      <div class="payroll-detail-header">
        <div>
          <h2>工资明细</h2>
          <p>锁定后本月工资不能重新计算，只能导出。</p>
        </div>
        <div class="payroll-detail-summary">
          <div>
            <span>员工数</span>
            <strong>{{ activeRun?.employee_count || 0 }}</strong>
          </div>
          <div>
            <span>实发合计</span>
            <strong>{{ netTotal }}</strong>
          </div>
          <div>
            <span>应发合计</span>
            <strong>{{ grossTotal }}</strong>
          </div>
          <div>
            <span>状态</span>
            <strong>{{ statusText(activeRun?.status) }}</strong>
          </div>
        </div>
      </div>
      <ResultsTable v-if="detail?.results.length" :exceptions="detail.exceptions" :results="detail.results" embedded />
      <div v-else class="empty-state">
        <FileSpreadsheet :size="38" aria-hidden="true" />
        <p>当前月份还没有核算结果，请导入 Excel 或同步钉钉并计算。</p>
      </div>
    </section>

    <div class="payroll-secondary-grid">
      <section class="panel monthly-history-panel">
        <div class="panel-header">
          <div>
            <h2>核算历史</h2>
            <p>展示本月最近生成的工资版本，可切换查看明细或导出。</p>
          </div>
          <span class="status-chip neutral">{{ runs.length }} 条记录</span>
        </div>

        <div v-if="runs.length" class="table-wrap">
          <table class="data-table monthly-history-table">
            <thead>
              <tr>
                <th>工资月份</th>
                <th>来源</th>
                <th>状态</th>
                <th>员工数</th>
                <th>实发合计</th>
                <th>计算时间</th>
                <th>操作</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="run in runs" :key="run.id" :class="{ 'is-selected': activeRun?.id === run.id }">
                <td>
                  <strong>{{ run.salary_month }}</strong>
                  <span v-if="activeRun?.id === run.id">当前查看</span>
                </td>
                <td>{{ sourceText(run.source_type) }}</td>
                <td><span class="status-chip" :class="run.status === 'locked' ? 'green' : 'neutral'">{{ statusText(run.status) }}</span></td>
                <td>{{ run.employee_count }}</td>
                <td class="mono strong">{{ money(run.net_total) }}</td>
                <td>{{ dateTime(run.created_at) }}</td>
                <td>
                  <button class="link-button" type="button" :disabled="loading || activeRun?.id === run.id" @click="viewRun(run)">
                    查看明细
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <div v-else class="empty-state">
          <FileSpreadsheet :size="38" aria-hidden="true" />
          <p>当前月份还没有核算历史，请导入 Excel 或同步钉钉并计算。</p>
        </div>
      </section>

      <section v-if="detail?.exceptions.length" class="panel payroll-exception-panel">
        <div class="panel-header">
          <div>
            <h2>异常清单</h2>
            <p>优先处理缺薪资档案、缺卡、迟到扣款和未匹配员工。</p>
          </div>
        </div>
        <div class="table-wrap">
          <table class="data-table">
            <thead>
              <tr>
                <th>员工</th>
                <th>类型</th>
                <th>级别</th>
                <th>说明</th>
                <th>状态</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in detail.exceptions" :key="item.id">
                <td>
                  <strong>{{ item.employee_name || "-" }}</strong>
                  <span>{{ item.employee_no || "-" }}</span>
                </td>
                <td>{{ exceptionTypeText(item.exception_type) }}</td>
                <td>{{ severityText(item.severity) }}</td>
                <td>{{ item.message }}</td>
                <td>{{ exceptionStatusText(item.status) }}</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>
    </div>

    <details v-if="activeRun" class="panel monthly-technical-panel">
      <summary>技术信息</summary>
      <p class="technical-note">用于系统排查和审计，日常工资核对不需要使用这些字段。</p>
      <dl class="detail-grid">
        <div>
          <dt>系统记录ID</dt>
          <dd>{{ activeRun.id }}</dd>
        </div>
        <div>
          <dt>来源任务编号</dt>
          <dd class="mono">{{ activeRun.source_job_id || "-" }}</dd>
        </div>
        <div>
          <dt>创建时间</dt>
          <dd>{{ dateTime(activeRun.created_at) }}</dd>
        </div>
        <div>
          <dt>更新时间</dt>
          <dd>{{ dateTime(activeRun.updated_at) }}</dd>
        </div>
      </dl>
    </details>
  </div>
</template>
