/**
 * Xuất báo cáo ra Excel.
 *
 *   /api/xuat-excel?loai=bao-cao&tu=2026-08-01&den=2026-08-31
 *   /api/xuat-excel?loai=khach-hang
 *   /api/xuat-excel?loai=don-hang&tu=…&den=…
 *   /api/xuat-excel?loai=phan-tich&ky=2026-08
 *
 * Dữ liệu luôn đi qua cùng tầng truy vấn với màn hình web, nên file xuất ra khớp
 * đúng những gì người dùng đang nhìn thấy — kể cả phạm vi dữ liệu theo vai trò.
 */
import { NextResponse } from "next/server";
import { and, desc, eq } from "drizzle-orm";
import { getSession } from "@/lib/auth";
import { withFocus } from "@/lib/focus";
import { db } from "@/db";
import {
  orders,
  orderLines,
  products,
  customers,
  users,
  territories,
  channels,
  alerts,
} from "@/db/schema";
import { loadCustomerList, parseCustomerListQuery, SORTS } from "@/lib/customer-list";
import { loadLeadList, parseLeadQuery } from "@/lib/lead-list";
import { loadOrderList, orderListConditions, parseOrderListQuery } from "@/lib/order-list";
import { orderStatusBadge } from "@/lib/format";
import { LEAD_LABEL } from "@/lib/lead-status";
import {
  coreMetrics,
  revenueByTerritory,
  revenueBySalesUser,
  revenueByProduct,
  promoGoodsByStaff,
  dealerActivity,
} from "@/lib/reports";
import { periodRange, monthKey, shiftMonth, today, isRealDate, isRealMonth, dayInTz } from "@/lib/period";
import { debtSummary, kpiForPeriod, salesLeaderboard, targetFor, type LeaderboardSort } from "@/lib/reports";
import { CUSTOMER_RANK_SORTS, CUSTOMER_STAGE_FILTERS, customerOverview, customerRanking, newCustomerList, newCustomersByGroup, type CustomerRankSort, type CustomerStageFilter } from "@/lib/customer-report";
import { courseCompletions, transferByGroup, transferList, transferOverview, transferTrend, type TrendGranularity } from "@/lib/transfer-report";
import { orderKinds } from "@/lib/event-kinds";
import { forecastTable } from "@/lib/forecast";
import { alertScope } from "@/lib/alerts-scope";
import { canManageSettings, canViewAllStaff } from "@/lib/rbac";
import { loadAuditForExport, parseAuditQuery, type AuditEntry } from "@/lib/audit-view";
import { monthlyTrend, planVsActual, productReport, promoReport, salesOverview, shareByTree, staffingReport, type TreeRow } from "@/lib/sales-analysis";
import { buildWorkbook, defineSheet, safeFileName, type AnySheetSpec } from "@/lib/excel";
import { ALERT_TYPE_LABEL, formatDateTime, formatPeriod, STAGE_LABEL, PRODUCT_ROLE_LABEL } from "@/lib/format";

export const dynamic = "force-dynamic";

export async function GET(request: Request) {
  const baseSession = await getSession();
  if (!baseSession) {
    return NextResponse.json({ error: "Chưa đăng nhập." }, { status: 401 });
  }

  const url = new URL(request.url);
  // File xuất phải khớp với màn hình đang xem, kể cả khi đã thu hẹp vào một khu vực/phòng/nhân viên.
  const session = withFocus(baseSession, url.searchParams.get("pv") ?? undefined);
  const kind = url.searchParams.get("loai") ?? "bao-cao";
  // Danh sách khách hàng dùng `ky` cho "kỳ tính" (thang/quy/nam...), các loại còn lại `ky` là kỳ báo cáo YYYY-MM.
  const requestedPeriod = url.searchParams.get("ky");
  const period = kind !== "khach-hang" && kind !== "khach-tiem-nang" && requestedPeriod ? requestedPeriod : monthKey(today());
  const tu = url.searchParams.get("tu");
  const den = url.searchParams.get("den");
  // Phân tích nhận thêm kỳ quý ('2026-Q3') và năm ('2026').
  const periodOk = kind === "phan-tich" ? isRealMonth(period) || /^\d{4}-Q[1-4]$/.test(period) || /^\d{4}$/.test(period) : isRealMonth(period);
  if (!periodOk || (tu && !isRealDate(tu)) || (den && !isRealDate(den))) {
    return NextResponse.json({ error: "Kỳ hoặc ngày không hợp lệ." }, { status: 400 });
  }
  // Phân tích là báo cáo quản trị (số liệu cả đội) — cùng quyền với trang Phân tích.
  if (kind === "phan-tich" && baseSession.role === "SALES") {
    return NextResponse.json({ error: "Bạn không có quyền xuất báo cáo phân tích." }, { status: 403 });
  }
  const defaults = periodRange(period);
  const start = tu || defaults.start;
  const end = den || defaults.end;
  const range = { start, end };

  let sheets: AnySheetSpec[];
  let title: string;
  let fileName: string;

  switch (kind) {
    case "khach-hang": {
      title = "Danh sách khách hàng và phân hạng đại lý";
      fileName = `khach-hang-${period}`;
      sheets = [await customerSheet(session, url.searchParams)];
      break;
    }
    case "khach-tiem-nang": {
      title = "Danh sách khách tiềm năng";
      fileName = `khach-tiem-nang-${today()}`;
      sheets = [await leadSheet(session, url.searchParams)];
      break;
    }
    case "don-hang": {
      title = "Chi tiết đơn hàng";
      fileName = `don-hang-${start}-den-${end}`;
      sheets = await orderSheets(session, range, url.searchParams);
      break;
    }
    case "phan-tich": {
      title = `Phân tích bán hàng ${formatPeriod(period)}`;
      fileName = `phan-tich-${period}`;
      // Tiêu chí xếp hạng nhân viên (xh): doanhthu | doanhso | no | moi — file Excel xếp đúng như màn hình đang xem.
      const sortParam = url.searchParams.get("xh");
      const sort: LeaderboardSort = ((["doanhthu", "doanhso", "no", "moi"] as const).find((k) => k === sortParam)) ?? "doanhthu";
      // Xếp hạng khách hàng (xk: doanhthu | doanhso | no) và loại khách (lk: DEALER | PROSPECT | LEAD) — file khớp màn hình Phân tích → Khách hàng.
      const customerSort: CustomerRankSort = CUSTOMER_RANK_SORTS.find((k) => k === url.searchParams.get("xk")) ?? "doanhthu";
      const stageParam = url.searchParams.get("lk") ?? "";
      const stage: CustomerStageFilter = (CUSTOMER_STAGE_FILTERS as readonly string[]).includes(stageParam) ? (stageParam as CustomerStageFilter) : "";
      sheets = await analyticsSheets(session, period, sort, customerSort, stage);
      break;
    }
    case "canh-bao":
    case "du-bao": {
      // Cảnh báo và dự báo dành cho Ban giám đốc, GĐKV, quản lý — cùng quyền với trang Cảnh báo.
      if (!canViewAllStaff(baseSession)) return NextResponse.json({ error: "Bạn không có quyền xuất cảnh báo." }, { status: 403 });
      if (kind === "du-bao") {
        const target = requestedPeriod && isRealMonth(requestedPeriod) ? requestedPeriod : shiftMonth(monthKey(today()), -1);
        title = `Dự báo dự trù sản phẩm ${formatPeriod(target)}`;
        fileName = `du-bao-${target}`;
        sheets = [await forecastSheet(session, target)];
      } else {
        title = "Cảnh báo đang mở";
        fileName = `canh-bao-${today()}`;
        sheets = [await alertSheet(session)];
      }
      break;
    }
    case "nhat-ky": {
      // Nhật ký thao tác chỉ Ban giám đốc xem — file xuất cùng quyền và cùng bộ lọc với màn hình.
      if (!canManageSettings(baseSession)) return NextResponse.json({ error: "Bạn không có quyền xuất nhật ký." }, { status: 403 });
      title = "Nhật ký thao tác";
      fileName = `nhat-ky-${today()}`;
      sheets = [await auditSheet(session, url.searchParams)];
      break;
    }
    case "chuyen-giao": {
      title = "Báo cáo chuyển giao";
      fileName = `chuyen-giao-${start}-den-${end}`;
      sheets = await transferSheets(session, range);
      break;
    }
    default: {
      title = "Báo cáo bán hàng";
      fileName = `bao-cao-${start}-den-${end}`;
      sheets = await reportSheets(session, range);
    }
  }

  const buffer = await buildWorkbook(sheets, { title, author: session.tenantName });

  return new NextResponse(new Uint8Array(buffer), {
    headers: {
      "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "Content-Disposition": `attachment; filename="${safeFileName(fileName)}.xlsx"`,
      "Cache-Control": "no-store",
    },
  });
}

/* -------------------------------------------------------------------------- */

type Session = NonNullable<Awaited<ReturnType<typeof getSession>>>;
type Range = { start: string; end: string };

async function reportSheets(session: Session, range: Range): Promise<AnySheetSpec[]> {
  const subtitle = `Từ ${vnDate(range.start)} đến ${vnDate(range.end)} · ${session.tenantName}`;

  const [metrics, byTerritory, bySales, byProduct, byStaffPromo, dealers, promo, staffing, customerStats, newCustomers] = await Promise.all([
    coreMetrics(session, range),
    revenueByTerritory(session, range),
    revenueBySalesUser(session, range),
    revenueByProduct(session, range, 500),
    promoGoodsByStaff(session, range),
    dealerActivity(session, range),
    promoReport(session, range),
    staffingReport(session, monthKey(range.end), range),
    customerOverview(session, range, ""),
    newCustomerList(session, range, "", { page: 1, pageSize: 1_000_000 }),
  ]);

  const summary = defineSheet<{ label: string; value: number | string }>({
    name: "Tổng hợp",
    subtitle,
    columns: [
      { header: "Chỉ số", value: (r) => r.label, width: 32 },
      { header: "Giá trị", value: (r) => r.value, type: "number", width: 20 },
    ],
    rows: [
      { label: "Doanh thu (đ)", value: metrics.revenue },
      { label: "Giá trị hàng khuyến mãi (đ)", value: metrics.promoValue },
      { label: "Số đơn hàng", value: metrics.orderCount },
      { label: "Số SKUs đã bán", value: metrics.skuCount },
      { label: "Số đại lý lấy hàng", value: metrics.dealerCount },
      { label: "Khách hàng mở mới", value: customerStats.newCount },
      { label: "Tổng khách hàng (đến hết kỳ)", value: customerStats.total },
      { label: "Tỷ lệ mở mới / tổng khách", value: customerStats.ratioPct != null ? `${customerStats.ratioPct.toFixed(1)}%` : "—" },
      { label: "Tổng đại lý đang quản lý", value: dealers.total },
      { label: "Đại lý không lấy hàng", value: dealers.inactive.length },
      { label: "Định biên NVKD", value: staffing.overall.headcount },
      { label: "Doanh thu bình quân / NVKD (đ)", value: Math.round(staffing.overall.perHead ?? 0) },
    ],
  });

  const territorySheet = defineSheet<(typeof byTerritory)[number]>({
    name: "Theo khu vực",
    subtitle,
    totals: true,
    columns: [
      { header: "Khu vực", value: (r) => r.territoryName },
      { header: "Số đơn", value: (r) => Number(r.orderCount), type: "number" },
      { header: "Số khách hàng", value: (r) => Number(r.customerCount), type: "number" },
      { header: "Doanh thu", value: (r) => Number(r.revenue), type: "money" },
    ],
    rows: byTerritory,
  });

  const salesSheet = defineSheet<(typeof bySales)[number]>({
    name: "Theo NVKD",
    subtitle,
    totals: true,
    columns: [
      { header: "Nhân viên", value: (r) => r.fullName },
      { header: "Số đơn", value: (r) => Number(r.orderCount), type: "number" },
      { header: "Số khách hàng", value: (r) => Number(r.customerCount), type: "number" },
      { header: "Doanh thu", value: (r) => Number(r.revenue), type: "money" },
    ],
    rows: bySales,
  });

  const productSheet = defineSheet<(typeof byProduct)[number]>({
    name: "Theo mặt hàng",
    subtitle,
    totals: true,
    columns: [
      { header: "Mã SP", value: (r) => r.code },
      { header: "Tên sản phẩm", value: (r) => r.name, width: 38 },
      { header: "Nhóm vai trò", value: (r) => PRODUCT_ROLE_LABEL[r.role] ?? r.role },
      { header: "SL bán", value: (r) => Number(r.qtySold), type: "number" },
      { header: "SL khuyến mãi", value: (r) => Number(r.qtyPromo), type: "number" },
      { header: "Số KH mua", value: (r) => Number(r.customerCount), type: "number" },
      { header: "Doanh thu", value: (r) => Number(r.revenue), type: "money" },
    ],
    rows: byProduct,
  });

  const promoSheet = defineSheet<(typeof byStaffPromo)[number]>({
    name: "Hàng KM theo NVKD",
    subtitle,
    totals: true,
    columns: [
      { header: "Nhân viên", value: (r) => r.salesUserName },
      { header: "Mã SP", value: (r) => r.code },
      { header: "Tên sản phẩm", value: (r) => r.name, width: 38 },
      { header: "SL khuyến mãi", value: (r) => Number(r.qtyPromo), type: "number" },
      { header: "Giá trị khuyến mãi", value: (r) => Number(r.promoValue), type: "money" },
    ],
    rows: byStaffPromo,
  });

  const dealerRows = [...dealers.active, ...dealers.inactive];
  const dealerSheet = defineSheet<(typeof dealerRows)[number]>({
    name: "Đại lý lấy hàng",
    subtitle,
    columns: [
      { header: "Mã ĐL", value: (r) => r.code },
      { header: "Tên đại lý", value: (r) => r.orgName, width: 34 },
      { header: "Khu vực", value: (r) => r.territoryName ?? "" },
      { header: "NVKD phụ trách", value: (r) => r.ownerName ?? "" },
      { header: "ĐL Key", value: (r) => (r.isKey ? "Có" : "") },
      { header: "Số đơn", value: (r) => r.orderCount, type: "number" },
      { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
      { header: "Tình trạng", value: (r) => (r.orderCount > 0 ? "Có lấy hàng" : "Không lấy hàng") },
    ],
    rows: dealerRows,
  });

  return [
    summary,
    territorySheet,
    salesSheet,
    productSheet,
    promoTreeSheet(subtitle, "KM theo khu vực", promo.byTerritory, "Khu vực"),
    promoTreeSheet(subtitle, "KM theo phòng ban", promo.byDepartment, "Phòng ban / Team"),
    promoTreeSheet(subtitle, "KM theo nhân viên", promo.byStaff, "Nhân viên"),
    promoSheet,
    defineSheet<(typeof newCustomers.rows)[number]>({
      name: "Khách hàng mới",
      subtitle: `${subtitle} · ${newCustomers.total} khách mở mới`,
      columns: [
        { header: "Ngày tạo", value: (r) => vnDate(r.createdDate) },
        { header: "Mã KH", value: (r) => r.code },
        { header: "Khách hàng", value: (r) => r.orgName, width: 34 },
        { header: "Người liên hệ", value: (r) => r.contactName ?? "", width: 20 },
        { header: "Điện thoại", value: (r) => r.phone ?? "" },
        { header: "Loại", value: (r) => STAGE_LABEL[r.stage] ?? r.stage },
        { header: "Kênh", value: (r) => r.channelName ?? "" },
        { header: "Khu vực", value: (r) => r.territoryName ?? "", width: 18 },
        { header: "NVKD phụ trách", value: (r) => r.ownerName ?? "", width: 22 },
        { header: "Số đơn", value: (r) => r.orderCount, type: "number" },
        { header: "Trị giá đơn", value: (r) => r.revenue, type: "money" },
      ],
      rows: newCustomers.rows,
    }),
    dealerSheet,
  ];
}

async function customerSheet(session: Session, params: URLSearchParams): Promise<AnySheetSpec> {
  // Cùng bộ lọc + xếp hạng với màn hình Khách hàng — file xuất ra khớp đúng danh sách đang xem.
  const query = parseCustomerListQuery((key) => params.get(key));
  const { rows, range } = await loadCustomerList(session, query);
  const sort = SORTS.find((s) => s.key === query.sort);

  return defineSheet<(typeof rows)[number]>({
    name: "Khách hàng",
    subtitle: `Xếp theo ${sort?.label ?? ""} (${query.dir === "giam" ? "cao → thấp" : "thấp → cao"}) · kỳ tính: ${range.label} · ${session.tenantName}`,
    columns: [
      { header: "Mã KH", value: (r) => r.code },
      { header: "Tên cơ sở", value: (r) => r.orgName, width: 34 },
      { header: "Người liên hệ chính", value: (r) => r.contactName ?? "" },
      { header: "Điện thoại", value: (r) => r.phone ?? "" },
      { header: "Người phụ trách", value: (r) => r.ownerName ?? "" },
      { header: "Phường", value: (r) => r.ward ?? "" },
      { header: "Tỉnh", value: (r) => r.territoryName ?? "" },
      { header: "Kênh", value: (r) => r.channelName ?? "" },
      { header: "Trạng thái", value: (r) => STAGE_LABEL[r.stage] ?? r.stage },
      { header: "ĐL Key", value: (r) => (r.isKey ? "Có" : "") },
      { header: "Ngày lên đơn gần nhất", value: (r) => r.lastOrderDate ?? "" },
      { header: "Giá trị đơn gần nhất", value: (r) => r.lastOrderValue, type: "money" },
      { header: "Công nợ", value: (r) => Math.max(r.outstanding, 0), type: "money" },
      { header: "Nợ quá hạn", value: (r) => r.overdue, type: "money" },
      { header: "Cảnh báo", value: (r) => r.warnings.map((w) => w.label).join("; "), width: 36 },
      { header: `Doanh số BQ/tháng (${range.label})`, value: (r) => Math.round(r.salesAvg), type: "money" },
      { header: `Doanh thu BQ/tháng (${range.label})`, value: (r) => Math.round(r.revenueAvg), type: "money" },
      { header: `Công nợ BQ/tháng (${range.label})`, value: (r) => Math.round(r.debtAvg), type: "money" },
      { header: `Số lần lên đơn (${range.label})`, value: (r) => r.orderCount, type: "number" },
      { header: "Hạng doanh thu", value: (r) => r.revenueTier ?? "" },
      { header: "DT bình quân 12 tháng", value: (r) => Number(r.avgRevenue ?? 0), type: "money" },
      { header: "Hạng SKU", value: (r) => r.skuTier ?? "" },
      { header: "Số SP ứng dụng", value: (r) => Number(r.skuCount ?? 0), type: "number" },
    ],
    rows,
  });
}

async function leadSheet(session: Session, params: URLSearchParams): Promise<AnySheetSpec> {
  // Cùng bộ lọc với màn hình Khách tiềm năng — xuất toàn bộ kết quả, không cắt theo trang.
  const query = parseLeadQuery((key) => params.get(key));
  const { rows } = await loadLeadList(session, query, { all: true });
  return defineSheet<(typeof rows)[number]>({
    name: "Khách tiềm năng",
    subtitle: `${query.status ? `Trạng thái: ${LEAD_LABEL[query.status]} · ` : ""}${session.tenantName}`,
    columns: [
      { header: "Ngày thêm mới", value: (r) => vnDate(dayInTz(r.createdAt)) },
      { header: "Mã", value: (r) => r.code },
      { header: "Họ tên người liên hệ", value: (r) => r.contactName ?? "", width: 26 },
      { header: "Tên cơ sở", value: (r) => (r.contactName && r.orgName !== r.contactName ? r.orgName : ""), width: 26 },
      { header: "Điện thoại", value: (r) => r.phone ?? "" },
      { header: "Phường", value: (r) => r.ward ?? "" },
      { header: "Tỉnh / thành", value: (r) => r.territoryName ?? "" },
      { header: "Loại", value: (r) => r.channelName ?? "" },
      { header: "Nhu cầu tư vấn", value: (r) => r.leadNeed ?? "", width: 30 },
      { header: "Nguồn khách", value: (r) => r.leadSource ?? "" },
      { header: "Trạng thái chuyển đổi", value: (r) => LEAD_LABEL[r.leadStatus] ?? r.leadStatus, width: 22 },
      { header: "Báo cáo gần nhất", value: (r) => (r.lastReport ? `${vnDate(dayInTz(r.lastReport.at))}: ${r.lastReport.text}` : ""), width: 44 },
      { header: "Người phụ trách", value: (r) => r.ownerName ?? "Chưa phân công" },
    ],
    rows,
  });
}

async function orderSheets(session: Session, _range: Range, params: URLSearchParams): Promise<AnySheetSpec[]> {
  // Cùng bộ lọc (kỳ, trạng thái, VAT, người tạo, tìm kiếm) với màn hình Đơn hàng.
  const query = parseOrderListQuery((key) => params.get(key));
  const subtitle = `Từ ${vnDate(query.from)} đến ${vnDate(query.to)} · ${session.tenantName}`;
  const conditions = await orderListConditions(session, query, true);
  const list = await loadOrderList(session, query, { all: true });

  const rows = await db
    .select({
      code: orders.code,
      orderDate: orders.orderDate,
      status: orders.status,
      needsRevision: orders.needsRevision,
      customerCode: customers.code,
      customerName: customers.orgName,
      channelName: channels.name,
      territoryName: territories.name,
      salesName: users.fullName,
      productCode: products.code,
      productName: products.name,
      qtySold: orderLines.qtySold,
      qtyPromo: orderLines.qtyPromo,
      unitPrice: orderLines.unitPrice,
      amount: orderLines.amount,
    })
    .from(orderLines)
    .innerJoin(orders, eq(orderLines.orderId, orders.id))
    .innerJoin(customers, eq(orders.customerId, customers.id))
    .innerJoin(products, eq(orderLines.productId, products.id))
    .leftJoin(channels, eq(orders.channelId, channels.id))
    .leftJoin(territories, eq(orders.territoryId, territories.id))
    .leftJoin(users, eq(orders.salesUserId, users.id))
    .where(and(...conditions))
    .orderBy(desc(orders.orderDate), orders.code);

  return [
    defineSheet<(typeof list.rows)[number]>({
      name: "Danh sách đơn",
      subtitle,
      columns: [
        { header: "Ngày tạo đơn", value: (r) => vnDate(r.orderDate) },
        { header: "Mã đơn hàng", value: (r) => r.code },
        { header: "Mã KH", value: (r) => r.customerCode },
        { header: "Tên cơ sở", value: (r) => r.customerName, width: 32 },
        { header: "Người tạo đơn", value: (r) => r.createdByName ?? r.salesName ?? "" },
        { header: "NVKD", value: (r) => r.salesName ?? "" },
        { header: "Trạng thái", value: (r) => orderStatusBadge(r).label },
        { header: "Cần xuất VAT", value: (r) => (r.vatRequired ? "Có" : "") },
        { header: "Doanh số", value: (r) => (r.status === "CANCELLED" ? 0 : r.totalAmount), type: "money" },
        { header: "Doanh thu", value: (r) => (r.status === "CONFIRMED" ? r.totalAmount : 0), type: "money" },
        { header: "Phụ thu phí", value: (r) => r.surchargeAmount, type: "money" },
        { header: "Thanh toán", value: (r) => r.paid, type: "money" },
        { header: "Còn lại", value: (r) => r.remaining, type: "money" },
      ],
      rows: list.rows,
    }),
    defineSheet<(typeof rows)[number]>({
      name: "Chi tiết đơn hàng",
      subtitle,
      totals: true,
      columns: [
        { header: "Mã đơn", value: (r) => r.code },
        { header: "Ngày", value: (r) => r.orderDate, type: "text" },
        { header: "Mã KH", value: (r) => r.customerCode },
        { header: "Khách hàng", value: (r) => r.customerName, width: 32 },
        { header: "Kênh", value: (r) => r.channelName ?? "" },
        { header: "Khu vực", value: (r) => r.territoryName ?? "" },
        { header: "NVKD", value: (r) => r.salesName ?? "" },
        { header: "Mã SP", value: (r) => r.productCode },
        { header: "Sản phẩm", value: (r) => r.productName, width: 34 },
        { header: "SL bán", value: (r) => Number(r.qtySold), type: "number" },
        { header: "SL KM", value: (r) => Number(r.qtyPromo), type: "number" },
        { header: "Đơn giá", value: (r) => Number(r.unitPrice), type: "money" },
        { header: "Thành tiền", value: (r) => Number(r.amount), type: "money" },
        { header: "Trạng thái", value: (r) => (r.status === "CONFIRMED" ? "Đã duyệt" : r.status === "DRAFT" ? (r.needsRevision ? "Điều chỉnh" : "Chờ duyệt") : "Huỷ") },
      ],
      rows,
    }),
  ];
}

/** Đoán cách chia cột thời gian theo độ dài khoảng ngày (file xuất chỉ nhận tu/den): 1 ngày → 7 ngày, ≤ 7 ngày → theo ngày, ≤ 31 → theo tuần, còn lại → theo tháng. */
function guessGranularity(range: Range): TrendGranularity {
  const days = Math.round((Date.parse(`${range.end}T00:00:00Z`) - Date.parse(`${range.start}T00:00:00Z`)) / 86_400_000) + 1;
  return days <= 1 ? "ngay" : days <= 7 ? "tuan" : days <= 31 ? "thang" : days <= 93 ? "quy" : "nam";
}

async function transferSheets(session: Session, range: Range): Promise<AnySheetSpec[]> {
  const subtitle = `Từ ${vnDate(range.start)} đến ${vnDate(range.end)} · ${session.tenantName}`;
  const [overview, trend, byRegion, byDept, byStaff, list, courses] = await Promise.all([
    transferOverview(session, range),
    transferTrend(session, range, guessGranularity(range)),
    transferByGroup(session, range, "khu-vuc"),
    transferByGroup(session, range, "phong-ban"),
    transferByGroup(session, range, "nhan-vien"),
    transferList(session, range, { page: 1, pageSize: 1_000_000 }),
    courseCompletions(session, range),
  ]);
  const kinds = orderKinds([...overview.byKind.map((k) => k.kind), ...trend.kinds]);

  const summaryRows: { label: string; value: number | string }[] = [
    { label: "Số lớp", value: overview.totals.classes },
    { label: "Lượt mời", value: overview.totals.invited },
    { label: "Lượt tham dự", value: overview.totals.attended },
    { label: "Tỷ lệ tham dự", value: overview.totals.ratePct != null ? `${overview.totals.ratePct.toFixed(1)}%` : "—" },
    { label: "Đơn chốt sau lớp", value: overview.totals.orders },
    { label: "Doanh thu sau lớp", value: overview.totals.revenue },
    { label: "Người phụ trách", value: overview.totals.owners },
    { label: "Khách hoàn thành khóa chuyển giao", value: courses.totalCustomers },
    { label: "Lượt hoàn thành khóa", value: courses.totalCompletions },
  ];
  type Group = Awaited<ReturnType<typeof transferByGroup>>[number];
  const groupSheet = (name: string, rows: Group[], label: string) =>
    defineSheet<Group>({
      name,
      subtitle,
      columns: [
        { header: label, value: (r) => indented(r), width: 30 },
        { header: "Số lớp", value: (r) => r.classes, type: "number" },
        { header: "Lượt mời", value: (r) => r.invited, type: "number" },
        { header: "Lượt tham dự", value: (r) => r.attended, type: "number" },
        { header: "Tỷ lệ dự", value: (r) => (r.ratePct != null ? r.ratePct / 100 : null), type: "percent" },
        { header: "Đơn chốt", value: (r) => r.orders, type: "number" },
        { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
      ],
      rows,
    });

  return [
    defineSheet<(typeof summaryRows)[number]>({
      name: "Tổng hợp",
      subtitle,
      columns: [
        { header: "Chỉ số", value: (r) => r.label, width: 36 },
        { header: "Giá trị", value: (r) => r.value, type: "number", width: 20 },
      ],
      rows: summaryRows,
    }),
    defineSheet<(typeof overview.byKind)[number]>({
      name: "Theo loại lớp",
      subtitle,
      columns: [
        { header: "Loại lớp", value: (r) => r.kind, width: 32 },
        { header: "Số lớp", value: (r) => r.classes, type: "number" },
        { header: "Tỷ trọng", value: (r) => r.sharePct / 100, type: "percent" },
        { header: "Lượt mời", value: (r) => r.invited, type: "number" },
        { header: "Lượt tham dự", value: (r) => r.attended, type: "number" },
        { header: "Tỷ lệ dự", value: (r) => (r.ratePct != null ? r.ratePct / 100 : null), type: "percent" },
        { header: "Đơn chốt", value: (r) => r.orders, type: "number" },
        { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
      ],
      rows: overview.byKind,
    }),
    defineSheet<(typeof trend.buckets)[number]>({
      name: "Theo thời gian",
      subtitle: `${subtitle} · số lớp theo loại`,
      totals: true,
      columns: [
        { header: "Thời gian", value: (r) => r.label, width: 22 },
        ...kinds.map((kind) => ({ header: kind, value: (r: (typeof trend.buckets)[number]) => r.byKind[kind] ?? 0, type: "number" as const })),
        { header: "Tổng số lớp", value: (r) => r.total, type: "number" },
        { header: "Lượt tham dự", value: (r) => r.attended, type: "number" },
      ],
      rows: trend.buckets,
    }),
    groupSheet("Theo khu vực", byRegion, "Khu vực"),
    groupSheet("Theo phòng ban", byDept, "Phòng ban / Team"),
    groupSheet("Theo nhân viên", byStaff, "Nhân viên"),
    defineSheet<(typeof list.rows)[number]>({
      name: "Danh sách lớp",
      subtitle: `${subtitle} · ${list.total} lớp`,
      columns: [
        { header: "Ngày", value: (r) => vnDate(r.eventDate) },
        { header: "Lớp / chuyên đề", value: (r) => r.name, width: 38 },
        { header: "Loại lớp", value: (r) => r.kind, width: 26 },
        { header: "Khu vực", value: (r) => r.territoryName ?? "Toàn quốc" },
        { header: "Người phụ trách", value: (r) => r.ownerName ?? "", width: 22 },
        { header: "Lượt mời", value: (r) => r.invitedCount, type: "number" },
        { header: "Lượt tham dự", value: (r) => r.attendedCount, type: "number" },
        { header: "Tỷ lệ dự", value: (r) => (r.attendanceRate != null ? r.attendanceRate / 100 : null), type: "percent" },
        { header: "Đơn chốt", value: (r) => r.resultOrderCount, type: "number" },
        { header: "Doanh thu", value: (r) => r.resultRevenue, type: "money" },
        { header: "Ghi chú sau lớp", value: (r) => r.note ?? "", width: 50 },
      ],
      rows: list.rows,
    }),
    defineSheet<(typeof courses.rows)[number]>({
      name: "Khóa chuyển giao",
      subtitle,
      totals: true,
      columns: [
        { header: "Khóa chuyển giao", value: (r) => r.name, width: 38 },
        { header: "Lượt hoàn thành", value: (r) => r.completions, type: "number" },
        { header: "Số khách", value: (r) => r.customers, type: "number" },
        { header: "Học miễn phí", value: (r) => r.free, type: "number" },
      ],
      rows: courses.rows,
    }),
  ];
}

async function forecastSheet(session: Session, target: string): Promise<AnySheetSpec> {
  const rows = await forecastTable(session.tenantId, target);
  return defineSheet<(typeof rows)[number]>({
    name: "Dự trù",
    subtitle: `${formatPeriod(target)} · ${session.tenantName}`,
    totals: true,
    columns: [
      { header: "Mã SP", value: (r) => r.code },
      { header: "Sản phẩm", value: (r) => r.name, width: 38 },
      { header: "Nhóm", value: (r) => r.groupName, width: 20 },
      { header: "Vai trò", value: (r) => PRODUCT_ROLE_LABEL[r.role] ?? r.role },
      { header: "Bán tháng gần nhất", value: (r) => r.lastMonthQty, type: "number" },
      { header: "Bình quân 3 tháng", value: (r) => r.avgQty, type: "number" },
      { header: "Cùng kỳ năm trước", value: (r) => r.lastYearQty, type: "number" },
      { header: "Dự báo (máy tính)", value: (r) => r.forecastQty, type: "number" },
      { header: "Điều chỉnh", value: (r) => r.adjustedQty, type: "number" },
      { header: "Dự trù", value: (r) => r.planQty, type: "number" },
      { header: "Độ tin cậy", value: (r) => r.confidence, type: "percent" },
    ],
    rows,
  });
}

async function alertSheet(session: Session): Promise<AnySheetSpec> {
  const rows = await db
    .select()
    .from(alerts)
    .where(and(await alertScope(session), eq(alerts.status, "OPEN")))
    .orderBy(desc(alerts.severity), desc(alerts.createdAt));
  const SEVERITY = { CRITICAL: "Nghiêm trọng", WARNING: "Cảnh báo", INFO: "Lưu ý" } as const;
  return defineSheet<(typeof rows)[number]>({
    name: "Cảnh báo",
    subtitle: `Đang mở · ${session.tenantName}`,
    columns: [
      { header: "Mức độ", value: (r) => SEVERITY[r.severity] },
      { header: "Loại", value: (r) => ALERT_TYPE_LABEL[r.type] ?? r.type, width: 30 },
      { header: "Cảnh báo", value: (r) => r.title, width: 50 },
      { header: "Chi tiết", value: (r) => r.message, width: 70 },
      { header: "Kỳ", value: (r) => formatPeriod(r.periodKey) },
    ],
    rows,
  });
}

/** Nhật ký thao tác theo đúng bộ lọc trên màn hình, tối đa 5.000 dòng mới nhất. */
async function auditSheet(session: Session, params: URLSearchParams): Promise<AnySheetSpec> {
  const query = parseAuditQuery((key) => params.get(key) ?? undefined);
  const rows = await loadAuditForExport(session.tenantId, query);
  return defineSheet<AuditEntry>({
    name: "Nhật ký",
    subtitle: `${rows.length} thao tác${rows.length >= 5000 ? " (giới hạn 5.000 dòng mới nhất)" : ""} · ${session.tenantName}`,
    columns: [
      { header: "Thời điểm", value: (r) => formatDateTime(r.createdAt), width: 18 },
      { header: "Người thực hiện", value: (r) => r.userName ?? "Hệ thống", width: 24 },
      { header: "Email", value: (r) => r.userEmail ?? "", width: 28 },
      { header: "Hành động", value: (r) => r.actionLabel, width: 14 },
      { header: "Đối tượng", value: (r) => r.entityLabel, width: 24 },
      { header: "Tên / mã", value: (r) => r.headline, width: 30 },
      { header: "Chi tiết", value: (r) => r.details.join(" · "), width: 50 },
      { header: "Thay đổi (cũ → mới)", value: (r) => r.changes.map((c) => `${c.label}: ${c.from} → ${c.to}`).join("\n"), width: 60 },
    ],
    rows,
  });
}

type NewByGroupRow = Awaited<ReturnType<typeof newCustomersByGroup>>[number];

function newByGroupSheet(subtitle: string, name: string, rows: NewByGroupRow[], label: string): AnySheetSpec {
  return defineSheet<NewByGroupRow>({
    name,
    subtitle,
    columns: [
      { header: label, value: (r) => indented(r), width: 30 },
      { header: "Khách mở mới", value: (r) => r.newCount, type: "number" },
      { header: "Tổng khách", value: (r) => r.total, type: "number" },
      { header: "Tỷ lệ mới / tổng", value: (r) => (r.ratioPct != null ? r.ratioPct / 100 : null), type: "percent" },
      { header: "Khách có đơn trong kỳ", value: (r) => r.buyers, type: "number" },
    ],
    rows,
  });
}

type PromoTotals = Awaited<ReturnType<typeof promoReport>>["totals"];

/** Sheet khuyến mãi theo một chiều (khu vực / phòng ban / nhân viên) — dùng ở cả báo cáo và phân tích. */
function promoTreeSheet(subtitle: string, name: string, rows: TreeRow<PromoTotals>[], label: string): AnySheetSpec {
  return defineSheet<TreeRow<PromoTotals>>({
    name,
    subtitle,
    columns: [
      { header: label, value: (r) => indented(r), width: 30 },
      { header: "Số đơn có KM", value: (r) => r.orders, type: "number" },
      { header: "SL hàng KM", value: (r) => r.qty, type: "number" },
      { header: "Giá trị KM", value: (r) => r.value, type: "money" },
      { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
      { header: "KM / doanh thu", value: (r) => (r.ratioPct != null ? r.ratioPct / 100 : null), type: "percent" },
    ],
    rows,
  });
}

/** Hàng của bảng cây → tên có thụt lề để đọc được cấp cha/con trong Excel. */
const indented = (row: { name: string; depth: number }) => `${row.depth > 0 ? `${"    ".repeat(row.depth - 1)}└ ` : ""}${row.name}`;

async function analyticsSheets(session: Session, period: string, sort: LeaderboardSort, customerSort: CustomerRankSort, stage: CustomerStageFilter): Promise<AnySheetSpec[]> {
  const range = periodRange(period);
  const subtitle = `${formatPeriod(period)} · ${session.tenantName}`;

  const [overview, target, debt, staffing, trend, promo, products, leaderboard, kpis, planRegion, planDept, planStaff, shareRegion, shareDept, customerStats, customerRank, newCustomers, newByRegion, newByDept, newByStaff] = await Promise.all([
    salesOverview(session, range),
    targetFor(session, period),
    debtSummary(session, range, today()),
    staffingReport(session, period),
    monthlyTrend(session, period.slice(0, 4)),
    promoReport(session, range),
    productReport(session, range),
    salesLeaderboard(session, range, sort),
    kpiForPeriod(session, period),
    planVsActual(session, period, "khu-vuc"),
    planVsActual(session, period, "phong-ban"),
    planVsActual(session, period, "nhan-vien"),
    shareByTree(session, range, "khu-vuc"),
    shareByTree(session, range, "phong-ban"),
    customerOverview(session, range, stage),
    customerRanking(session, range, period, stage, customerSort, { page: 1, pageSize: 1_000_000 }),
    newCustomerList(session, range, stage, { page: 1, pageSize: 1_000_000 }),
    newCustomersByGroup(session, range, stage, "khu-vuc"),
    newCustomersByGroup(session, range, stage, "phong-ban"),
    newCustomersByGroup(session, range, stage, "nhan-vien"),
  ]);

  const summaryRows: { label: string; value: number | string }[] = [
    { label: "Doanh số (đơn đã lập chưa huỷ)", value: overview.sales },
    { label: "Số đơn đã lập", value: overview.salesOrders },
    { label: "Doanh thu (đơn đã duyệt)", value: overview.revenue },
    { label: "Số đơn đã duyệt", value: overview.revenueOrders },
    { label: "Đơn chờ duyệt", value: overview.pendingOrders },
    { label: "Kế hoạch doanh thu", value: target?.revenueTarget ?? "Chưa giao" },
    { label: "% hoàn thành kế hoạch", value: target && target.revenueTarget > 0 ? `${((overview.revenue / target.revenueTarget) * 100).toFixed(1)}%` : "—" },
    { label: "Đã thu", value: debt.collected },
    { label: "Còn phải thu", value: debt.outstanding },
    { label: "Quá hạn", value: debt.overdue },
    { label: "Giá trị hàng khuyến mãi", value: promo.totals.value },
    { label: "Định biên NVKD", value: staffing.overall.headcount },
    { label: "Doanh thu / NVKD", value: Math.round(staffing.overall.perHead ?? 0) },
    { label: "Tổng khách hàng (đến hết kỳ)", value: customerStats.total },
    { label: "Khách hàng mở mới trong kỳ", value: customerStats.newCount },
    { label: "Tỷ lệ mở mới / tổng khách", value: customerStats.ratioPct != null ? `${customerStats.ratioPct.toFixed(1)}%` : "—" },
    { label: "Khách có đơn trong kỳ", value: customerStats.buyers },
  ];

  const planByUser = new Map(planStaff.rows.map((r) => [r.id, r]));

  const planSheet = (name: string, plan: Awaited<ReturnType<typeof planVsActual>>, label: string) =>
    defineSheet<TreeRow<Awaited<ReturnType<typeof planVsActual>>["rows"][number]>>({
      name,
      subtitle,
      columns: [
        { header: label, value: (r) => indented(r), width: 30 },
        { header: "Kế hoạch", value: (r) => r.target, type: "money" },
        { header: "Thực hiện (doanh thu)", value: (r) => r.actual, type: "money" },
        { header: "% Hoàn thành", value: (r) => (r.pct != null ? r.pct / 100 : null), type: "percent" },
        { header: "Chênh lệch (KH − TH)", value: (r) => r.gap, type: "money" },
        { header: "Doanh số", value: (r) => r.sales ?? 0, type: "money" },
        { header: "Tỷ trọng", value: (r) => r.sharePct / 100, type: "percent" },
      ],
      rows: plan.rows,
    });

  const shareSheet = (name: string, share: Awaited<ReturnType<typeof shareByTree>>, label: string) =>
    defineSheet<TreeRow<Awaited<ReturnType<typeof shareByTree>>["rows"][number]>>({
      name,
      subtitle,
      columns: [
        { header: label, value: (r) => indented(r), width: 30 },
        { header: "Số đơn", value: (r) => r.orders, type: "number" },
        { header: "Doanh số", value: (r) => r.sales ?? 0, type: "money" },
        { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
        { header: "Tỷ trọng", value: (r) => r.sharePct / 100, type: "percent" },
      ],
      rows: share.rows,
    });

  return [
    defineSheet<(typeof summaryRows)[number]>({
      name: "Tổng hợp",
      subtitle,
      columns: [
        { header: "Chỉ số", value: (r) => r.label, width: 36 },
        { header: "Giá trị", value: (r) => r.value, type: "number", width: 22 },
      ],
      rows: summaryRows,
    }),
    defineSheet<(typeof trend)[number]>({
      name: "Theo tháng",
      subtitle: `Năm ${period.slice(0, 4)} · ${session.tenantName}`,
      totals: true,
      columns: [
        { header: "Tháng", value: (r) => formatPeriod(r.month) },
        { header: "Số đơn duyệt", value: (r) => r.orders, type: "number" },
        { header: "Doanh số", value: (r) => r.sales, type: "money" },
        { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
        { header: "Đã thu", value: (r) => r.collected, type: "money" },
        { header: "Còn phải thu", value: (r) => r.outstanding, type: "money" },
      ],
      rows: trend,
    }),
    planSheet("Kế hoạch khu vực", planRegion, "Khu vực"),
    planSheet("Kế hoạch phòng ban", planDept, "Phòng ban / Team"),
    planSheet("Kế hoạch nhân viên", planStaff, "Nhân viên"),
    shareSheet("Tỷ trọng khu vực", shareRegion, "Khu vực"),
    shareSheet("Tỷ trọng phòng ban", shareDept, "Phòng ban / Team"),
    defineSheet<(typeof leaderboard)[number]>({
      name: "Xếp hạng nhân viên",
      subtitle: `${subtitle} · xếp theo ${{ doanhthu: "doanh thu", doanhso: "doanh số", no: "công nợ", moi: "khách mở mới" }[sort]}, cao → thấp`,
      columns: [
        { header: "Hạng", value: (r) => r.rank, type: "number" },
        { header: "Nhân viên", value: (r) => r.fullName, width: 26 },
        { header: "Chức vụ", value: (r) => r.jobTitle ?? "Nhân viên kinh doanh", width: 22 },
        { header: "Phòng ban", value: (r) => r.departmentName, width: 22 },
        { header: "Khu vực", value: (r) => r.territoryName ?? "", width: 16 },
        { header: "Số đơn", value: (r) => r.orderCount, type: "number" },
        { header: "Số KH", value: (r) => r.customerCount, type: "number" },
        { header: "KH mở mới", value: (r) => r.newCustomers, type: "number" },
        { header: "Doanh số", value: (r) => r.sales, type: "money" },
        { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
        { header: "Công nợ", value: (r) => r.debt, type: "money" },
        { header: "Chỉ tiêu", value: (r) => planByUser.get(r.userId ?? "")?.target ?? null, type: "money" },
        { header: "% Kế hoạch", value: (r) => { const p = planByUser.get(r.userId ?? "")?.pct; return p != null ? p / 100 : null; }, type: "percent" },
        { header: "Tỷ trọng", value: (r) => r.sharePct / 100, type: "percent" },
      ],
      rows: leaderboard,
    }),
    defineSheet<(typeof products.rows)[number]>({
      name: "Doanh số sản phẩm",
      subtitle,
      columns: [
        { header: "Mã SP", value: (r) => r.code },
        { header: "Sản phẩm", value: (r) => r.name, width: 36 },
        { header: "Nhóm SP", value: (r) => r.groupName, width: 20 },
        { header: "Vai trò", value: (r) => PRODUCT_ROLE_LABEL[r.role] ?? r.role },
        { header: "SL bán", value: (r) => r.qtySold, type: "number" },
        { header: "SL KM", value: (r) => r.qtyPromo, type: "number" },
        { header: "Số KH mua", value: (r) => r.customers, type: "number" },
        { header: "Doanh số", value: (r) => r.sales, type: "money" },
        { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
        { header: "Tỷ trọng", value: (r) => r.sharePct / 100, type: "percent" },
      ],
      rows: products.rows,
    }),
    promoTreeSheet(subtitle, "KM theo khu vực", promo.byTerritory, "Khu vực"),
    promoTreeSheet(subtitle, "KM theo phòng ban", promo.byDepartment, "Phòng ban / Team"),
    promoTreeSheet(subtitle, "KM theo nhân viên", promo.byStaff, "Nhân viên"),
    defineSheet<(typeof promo.byProduct)[number]>({
      name: "KM theo sản phẩm",
      subtitle,
      totals: true,
      columns: [
        { header: "Mã SP", value: (r) => r.code },
        { header: "Sản phẩm", value: (r) => r.name, width: 36 },
        { header: "SL hàng KM", value: (r) => r.qty, type: "number" },
        { header: "Giá trị KM", value: (r) => r.value, type: "money" },
      ],
      rows: promo.byProduct,
    }),
    defineSheet<(typeof staffing.byStaff)[number]>({
      name: "Định biên NVKD",
      subtitle: `${subtitle} · định biên ${staffing.overall.headcount} NVKD`,
      columns: [
        { header: "Nhân viên", value: (r) => r.name, width: 26 },
        { header: "Team", value: (r) => r.departmentName, width: 22 },
        { header: "Số đơn", value: (r) => r.orders, type: "number" },
        { header: "Doanh số", value: (r) => r.sales, type: "money" },
        { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
        { header: "So với bình quân", value: (r) => (staffing.overall.perHead ? r.revenue / staffing.overall.perHead : null), type: "percent" },
      ],
      rows: staffing.byStaff,
    }),
    defineSheet<(typeof customerRank.rows)[number]>({
      name: "Xếp hạng khách hàng",
      subtitle: `${subtitle} · xếp theo ${{ doanhthu: "doanh thu", doanhso: "doanh số", no: "công nợ" }[customerSort]}, cao → thấp`,
      columns: [
        { header: "Hạng", value: (r) => r.rank, type: "number" },
        { header: "Mã KH", value: (r) => r.code },
        { header: "Khách hàng", value: (r) => r.orgName, width: 34 },
        { header: "Loại", value: (r) => STAGE_LABEL[r.stage] ?? r.stage },
        { header: "Hạng ĐL", value: (r) => r.tier ?? "" },
        { header: "Khu vực", value: (r) => r.territoryName ?? "", width: 18 },
        { header: "NVKD phụ trách", value: (r) => r.ownerName ?? "", width: 22 },
        { header: "Số đơn", value: (r) => r.orderCount, type: "number" },
        { header: "Doanh số", value: (r) => r.sales, type: "money" },
        { header: "Doanh thu", value: (r) => r.revenue, type: "money" },
        { header: "Công nợ", value: (r) => r.debt, type: "money" },
        { header: "Đơn gần nhất", value: (r) => (r.lastOrderDate ? vnDate(r.lastOrderDate) : "") },
      ],
      rows: customerRank.rows,
    }),
    defineSheet<(typeof newCustomers.rows)[number]>({
      name: "Khách hàng mới",
      subtitle: `${subtitle} · ${newCustomers.total} khách mở mới`,
      columns: [
        { header: "Ngày tạo", value: (r) => vnDate(r.createdDate) },
        { header: "Mã KH", value: (r) => r.code },
        { header: "Khách hàng", value: (r) => r.orgName, width: 34 },
        { header: "Người liên hệ", value: (r) => r.contactName ?? "", width: 20 },
        { header: "Điện thoại", value: (r) => r.phone ?? "" },
        { header: "Loại", value: (r) => STAGE_LABEL[r.stage] ?? r.stage },
        { header: "Kênh", value: (r) => r.channelName ?? "" },
        { header: "Khu vực", value: (r) => r.territoryName ?? "", width: 18 },
        { header: "NVKD phụ trách", value: (r) => r.ownerName ?? "", width: 22 },
        { header: "Số đơn", value: (r) => r.orderCount, type: "number" },
        { header: "Trị giá đơn", value: (r) => r.revenue, type: "money" },
      ],
      rows: newCustomers.rows,
    }),
    newByGroupSheet(subtitle, "Khách mới theo khu vực", newByRegion, "Khu vực"),
    newByGroupSheet(subtitle, "Khách mới theo phòng ban", newByDept, "Phòng ban / Team"),
    newByGroupSheet(subtitle, "Khách mới theo nhân viên", newByStaff, "Nhân viên"),
    // KPI đi qua `kpiForPeriod` (đã lọc theo phạm vi người xem và bộ chọn) — trước đây truy vấn thẳng bảng KPI nên xuất ra KPI cả công ty.
    defineSheet<(typeof kpis)[number]>({
      name: "KPI nhân viên",
      subtitle,
      columns: [
        { header: "Nhân viên", value: (r) => r.fullName, width: 26 },
        { header: "Doanh thu", value: (r) => Number(r.revenue), type: "money" },
        { header: "Chỉ tiêu", value: (r) => Number(r.revenueTarget), type: "money" },
        { header: "% Hoàn thành", value: (r) => Number(r.achievementPct) / 100, type: "percent" },
        { header: "Số đơn", value: (r) => Number(r.orderCount), type: "number" },
        { header: "Lượt viếng thăm", value: (r) => Number(r.visitCount), type: "number" },
        { header: "% Bám kế hoạch", value: (r) => Number(r.planCompliancePct) / 100, type: "percent" },
        { header: "KH mở mới", value: (r) => Number(r.newCustomerCount), type: "number" },
        { header: "ĐL hoạt động", value: (r) => Number(r.activeDealerCount), type: "number" },
      ],
      rows: kpis.filter((k) => Number(k.revenueTarget) > 0 || Number(k.revenue) > 0 || k.orderCount > 0),
    }),
  ];
}

function vnDate(iso: string): string {
  const [y, m, d] = iso.split("-");
  return `${d}/${m}/${y}`;
}
