"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import {
  CalendarDays,
  CircleDollarSign,
  Database,
  FileDown,
  FileSpreadsheet,
  ListFilter,
  Search,
  ShieldAlert,
  ShieldCheck,
  Ticket,
  Users,
  WalletCards,
} from "lucide-react";

import { getAdminAuditLogs } from "@/api/admin-audit";
import { useAdminPlayCodeLabel, useAdminPlayTypeCatalog } from "@/hooks/use-admin-play-type-catalog";
import { useCachedPlayTypeOptions } from "@/hooks/use-cached-play-type-options";
import { useAsyncEffect } from "@/hooks/use-async-effect";
import { useTranslationRef } from "@/hooks/use-translation-ref";
import { getAdminDraws, getAdminDrawFinanceSummary } from "@/api/admin-draws";
import { getAdminPlayers } from "@/api/admin-player";
import { downloadAdminReportJob, postAdminReportJob } from "@/api/admin-report-jobs";
import {
  getAdminReportDailyProfit,
  getAdminReportPlayDimension,
  getAdminReportPlayerWinLoss,
  getAdminReportRebateCommission,
} from "@/api/admin-reports";
import {
  buildReportJobParameters,
  REPORT_UI_SERVER_FULL_EXPORT,
  REPORT_UI_TO_JOB_TYPE,
  type ReportUiKey,
} from "@/lib/report-export-map";
import { getAdminRiskPoolDetail, getAdminRiskPools } from "@/api/admin-risk";
import { getAdminUsers } from "@/api/admin-users";
import { getAdminTransferOrders } from "@/api/admin-wallet";
import { adminHasAnyPermission } from "@/lib/admin-permissions";
import { PRD_REPORT_EXPORT, PRD_REPORT_VIEW } from "@/lib/admin-prd";
import { useAdminProfile } from "@/stores/admin-session";
import { adminAgentDisplayLabel } from "@/components/admin/admin-agent-columns";
import { AdminNoResourceState, AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
import { AdminDateRangeField } from "@/components/admin/admin-date-range-field";
import { AdminListPaginationFooter } from "@/components/admin/admin-list-pagination-footer";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { useAdminCurrencyCatalog } from "@/hooks/use-admin-currency-catalog";
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { formatAdminInstant } from "@/lib/admin-datetime";
import { getAdminRequestLocale } from "@/lib/admin-locale";
import { signedMoneyClass } from "@/lib/admin-signed-money";
import { cn } from "@/lib/utils";
import { formatAdminMinorUnits } from "@/lib/money";
import { LotteryApiBizError } from "@/types/api/errors";
import type { AdminAuditLogRow } from "@/types/api/admin-audit";
import type { AdminDrawListItem } from "@/types/api/admin-draws";
import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance";
import type { AdminPlayerRow } from "@/types/api/admin-player";
import type { AdminRiskPoolRow, AdminRiskPoolShowData } from "@/types/api/admin-risk";
import type { AdminUserPermissionRow } from "@/types/api/admin-user";
import type { AdminTransferOrderItem } from "@/types/api/admin-wallet";
import type {
  AdminReportDailyProfitRow,
  AdminReportPlayDimensionRow,
  AdminReportPlayerWinLossRow,
  AdminReportRebateCommissionRow,
} from "@/types/api/admin-reports";

export type ReportCategory = "profit" | "wallet" | "risk" | "audit";
type FilterKind = "draw" | "date" | "player_period" | "draw_number" | "play" | "play_period" | "operator_period";
type FieldKey = "drawNo" | "number" | "player" | "play" | "operator" | "period";
type ExportFormat = "csv" | "excel";
type ExportCell = string | number | null;
type ExportRow = Record<string, ExportCell>;
type SearchKind = "draw" | "player" | "operator";

type ReportKey =
  | "draw_profit"
  | "daily_profit"
  | "player_win_loss"
  | "player_transfer"
  | "hot_number_risk"
  | "play_dimension"
  | "sold_out_number"
  | "rebate_commission"
  | "admin_audit";

type ReportDefinition = {
  key: ReportKey;
  category: ReportCategory;
  icon: typeof FileSpreadsheet;
  filterKind: FilterKind;
  scope: string;
  fields: FieldKey[];
  connected: boolean;
};

type PreviewColumns = {
  primary: string;
  secondary: string;
  metricA: string;
  metricB: string;
  metricC: string;
  status: string;
  extra: string;
  time: string;
};

type ReportFilters = {
  drawNo: string;
  drawId: number | null;
  number: string;
  player: string;
  playerId: number | null;
  play: string;
  operator: string;
  operatorId: number | null;
  dateFrom: string;
  dateTo: string;
};

type ReportMeta = {
  total: number;
  page: number;
  perPage: number;
  lastPage: number;
};

type ReportResult =
  | { key: "draw_profit"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta | null; raw: AdminDrawFinanceSummaryData }
  | { key: "daily_profit"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta; raw: AdminReportDailyProfitRow[] }
  | { key: "player_win_loss"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta; raw: AdminReportPlayerWinLossRow[] }
  | { key: "player_transfer"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta; raw: AdminTransferOrderItem[] }
  | { key: "hot_number_risk"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta | null; raw: AdminRiskPoolShowData }
  | { key: "play_dimension"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta; raw: AdminReportPlayDimensionRow[] }
  | { key: "sold_out_number"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta; raw: AdminRiskPoolRow[] }
  | { key: "rebate_commission"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta; raw: AdminReportRebateCommissionRow[] }
  | { key: "admin_audit"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta; raw: AdminAuditLogRow[] };

type StatCard = {
  label: string;
  value: string;
  tone?: "default" | "good" | "warn" | "bad";
};

type SearchState = {
  open: SearchKind | null;
  query: string;
  loading: boolean;
  draws: AdminDrawListItem[];
  players: AdminPlayerRow[];
  operators: AdminUserPermissionRow[];
};

type PlayOption = {
  code: string;
  label: string;
};

const REPORTS: ReportDefinition[] = [
  { key: "draw_profit", category: "profit", icon: Ticket, filterKind: "draw", scope: "drawNo", fields: ["drawNo"], connected: true },
  { key: "daily_profit", category: "profit", icon: CalendarDays, filterKind: "date", scope: "date", fields: ["period"], connected: true },
  { key: "player_win_loss", category: "profit", icon: Users, filterKind: "player_period", scope: "playerPeriod", fields: ["player", "period"], connected: true },
  { key: "player_transfer", category: "wallet", icon: WalletCards, filterKind: "player_period", scope: "playerPeriod", fields: ["player", "period"], connected: true },
  { key: "hot_number_risk", category: "risk", icon: ShieldAlert, filterKind: "draw_number", scope: "drawNumber", fields: ["drawNo", "number"], connected: true },
  { key: "play_dimension", category: "profit", icon: ListFilter, filterKind: "play_period", scope: "playPeriod", fields: ["play", "period"], connected: true },
  { key: "sold_out_number", category: "risk", icon: ShieldCheck, filterKind: "draw", scope: "drawNo", fields: ["drawNo"], connected: true },
  { key: "rebate_commission", category: "profit", icon: CircleDollarSign, filterKind: "play_period", scope: "playPeriod", fields: ["play", "period"], connected: true },
  { key: "admin_audit", category: "audit", icon: FileSpreadsheet, filterKind: "operator_period", scope: "operatorPeriod", fields: ["operator", "period"], connected: true },
];

const emptyFilters: ReportFilters = {
  drawNo: "",
  drawId: null,
  number: "",
  player: "",
  playerId: null,
  play: "",
  operator: "",
  operatorId: null,
  dateFrom: "",
  dateTo: "",
};

const emptySearch: SearchState = {
  open: null,
  query: "",
  loading: false,
  draws: [],
  players: [],
  operators: [],
};

function categoryTone(category: ReportCategory): string {
  switch (category) {
    case "wallet":
      return "border-emerald-200 bg-emerald-50 text-emerald-700";
    case "risk":
      return "border-red-200 bg-red-50 text-red-700";
    case "audit":
      return "border-slate-200 bg-slate-50 text-slate-700";
    default:
      return "border-blue-200 bg-blue-50 text-blue-700";
  }
}

function statTone(tone: StatCard["tone"]): string {
  switch (tone) {
    case "good":
      return "border-emerald-200 bg-emerald-50/70 text-emerald-900";
    case "warn":
      return "border-amber-200 bg-amber-50/70 text-amber-950";
    case "bad":
      return "border-red-200 bg-red-50/70 text-red-950";
    default:
      return "border-border/70 bg-card text-foreground";
  }
}

function formatKind(kind: FilterKind, t: (key: string) => string): string {
  return t(`filters.${kind}`);
}

function downloadBlob(blob: Blob, filename: string): void {
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = filename;
  document.body.appendChild(anchor);
  anchor.click();
  anchor.remove();
  URL.revokeObjectURL(url);
}

function normalizeFilenamePart(value: string): string {
  return value.trim().replace(/[\\/:*?"<>|\s]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
}

function formatExportInstant(iso: string | null | undefined): ExportCell {
  return formatAdminInstant(iso, { locale: getAdminRequestLocale() });
}

function toCsvValue(value: ExportCell): string {
  if (value == null) {
    return "";
  }

  const stringValue = String(value);
  if (/[",\n]/.test(stringValue)) {
    return `"${stringValue.replace(/"/g, '""')}"`;
  }
  return stringValue;
}

async function exportRows(rows: ExportRow[], filename: string, sheetName: string, format: ExportFormat): Promise<void> {
  if (rows.length === 0) {
    throw new LotteryApiBizError("no_data", -1, null);
  }

  if (format === "csv") {
    const headers = Object.keys(rows[0]);
    const lines = [
      headers.map(toCsvValue).join(","),
      ...rows.map((row) => headers.map((header) => toCsvValue(row[header] ?? "")).join(",")),
    ];
    const blob = new Blob([`\uFEFF${lines.join("\n")}`], { type: "text/csv;charset=utf-8;" });
    downloadBlob(blob, `${filename}.csv`);
    return;
  }

  const XLSX = await import("xlsx");
  const worksheet = XLSX.utils.json_to_sheet(rows);
  const workbook = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
  XLSX.writeFile(workbook, `${filename}.xlsx`);
}

function buildDailyProfitRowsAndSummary(
  items: AdminReportDailyProfitRow[],
  total: number,
  t: (key: string) => string,
  pageScopedLabel: (statKey: string) => string,
): Pick<Extract<ReportResult, { key: "daily_profit" }>, "rows" | "summary"> {
  let totalBet = 0;
  let totalPayout = 0;
  let totalGross = 0;

  const rows = items.map((item) => {
    totalBet += item.total_bet_minor;
    totalPayout += item.total_payout_minor;
    totalGross += item.approx_house_gross_minor;
    return {
      business_date: item.business_date,
      total_bet_minor: item.total_bet_minor,
      total_payout_minor: item.total_payout_minor,
      approx_house_gross_minor: item.approx_house_gross_minor,
    };
  });

  return {
    rows,
    summary: [
      { label: t("preview.stats.records"), value: String(total) },
      { label: pageScopedLabel("bet"), value: formatPlainMoney(totalBet, "NPR") },
      { label: pageScopedLabel("payout"), value: formatPlainMoney(totalPayout, "NPR") },
      {
        label: pageScopedLabel("houseGross"),
        value: formatPlainMoney(totalGross, "NPR"),
        tone: totalGross >= 0 ? "good" : "bad",
      },
    ],
  };
}

function buildPlayerTransferRowsAndSummary(
  items: AdminTransferOrderItem[],
  total: number,
  page: number,
  perPage: number,
  t: (key: string) => string,
  pageScopedLabel: (statKey: string) => string,
): Pick<Extract<ReportResult, { key: "player_transfer" }>, "rows" | "summary" | "meta"> {
  let transferInCount = 0;
  let transferOutCount = 0;

  const rows = items.map((item) => {
    if (item.direction === "in") {
      transferInCount += 1;
    } else if (item.direction === "out") {
      transferOutCount += 1;
    }

    return {
      id: item.id,
      transfer_no: item.transfer_no,
      player_id: item.player_id,
      username: item.username,
      nickname: item.nickname,
      direction: item.direction,
      currency_code: item.currency_code,
      amount: item.amount,
      status: item.status,
      external_ref_no: item.external_ref_no,
      fail_reason: item.fail_reason,
      created_at: formatExportInstant(item.created_at),
      finished_at: formatExportInstant(item.finished_at),
    };
  });

  return {
    rows,
    meta: { total, page, perPage, lastPage: Math.max(1, Math.ceil(total / perPage)) },
    summary: [
      { label: t("preview.stats.records"), value: String(total) },
      { label: t("preview.stats.currentPage"), value: String(items.length) },
      { label: pageScopedLabel("transferIn"), value: String(transferInCount), tone: "good" },
      { label: pageScopedLabel("transferOut"), value: String(transferOutCount), tone: "warn" },
    ],
  };
}

function buildPlayDimensionRowsAndSummary(
  items: AdminReportPlayDimensionRow[],
  total: number,
  t: (key: string) => string,
  pageScopedLabel: (statKey: string) => string,
): Pick<Extract<ReportResult, { key: "play_dimension" }>, "rows" | "summary"> {
  let totalBet = 0;
  let totalPayout = 0;

  const rows = items.map((item) => {
    totalBet += item.total_bet_minor;
    totalPayout += item.total_payout_minor;
    return {
      play_code: item.play_code,
      dimension: item.dimension,
      total_bet_minor: item.total_bet_minor,
      total_payout_minor: item.total_payout_minor,
      approx_house_gross_minor: item.approx_house_gross_minor,
    };
  });

  return {
    rows,
    summary: [
      { label: t("preview.stats.records"), value: String(total) },
      { label: t("preview.stats.currentPage"), value: String(items.length) },
      { label: pageScopedLabel("bet"), value: formatPlainMoney(totalBet, "NPR") },
      { label: pageScopedLabel("payout"), value: formatPlainMoney(totalPayout, "NPR") },
    ],
  };
}

function buildRebateCommissionRowsAndSummary(
  items: AdminReportRebateCommissionRow[],
  total: number,
  t: (key: string) => string,
  pageScopedLabel: (statKey: string) => string,
): Pick<Extract<ReportResult, { key: "rebate_commission" }>, "rows" | "summary"> {
  let totalRebate = 0;
  let totalOrders = 0;

  const rows = items.map((item) => {
    totalRebate += item.total_rebate_minor;
    totalOrders += item.order_count;
    return {
      play_code: item.play_code,
      total_rebate_minor: item.total_rebate_minor,
      order_count: item.order_count,
      ticket_item_count: item.ticket_item_count,
    };
  });

  return {
    rows,
    summary: [
      { label: t("preview.stats.records"), value: String(total) },
      { label: t("preview.stats.currentPage"), value: String(items.length) },
      { label: pageScopedLabel("rebate"), value: formatPlainMoney(totalRebate, "NPR") },
      { label: pageScopedLabel("orders"), value: String(totalOrders) },
    ],
  };
}

function metaFromList(meta: { current_page: number; per_page: number; total: number; last_page: number }): ReportMeta {
  return {
    total: meta.total,
    page: meta.current_page,
    perPage: meta.per_page,
    lastPage: meta.last_page,
  };
}

function formatPlainMoney(value: number, currencyCode: string | null | undefined): string {
  return formatAdminMinorUnits(value, currencyCode || "NPR");
}

function signedProfitCell(amount: number, currencyCode: string | null | undefined): string {
  return cn("text-center tabular-nums", signedMoneyClass(amount, true));
}

function formatUsagePercent(ratio: number | null | undefined): string {
  return ratio == null ? "-" : `${Math.round(ratio * 100)}%`;
}

function optionText(...parts: Array<string | number | null | undefined>): string {
  return parts.filter((part) => part !== null && part !== undefined && String(part).trim() !== "").join(" / ");
}

function reportListParams(
  filters: ReportFilters,
  page: number,
  perPage: number,
) {
  return {
    page,
    per_page: perPage,
    date_from: filters.dateFrom || undefined,
    date_to: filters.dateTo || undefined,
    player_id: filters.playerId ?? undefined,
    play_code: filters.play.trim() || undefined,
  };
}

function parsePositiveInteger(value: string): number | null {
  const trimmed = value.trim();
  if (!/^\d+$/.test(trimmed)) {
    return null;
  }
  const parsed = Number(trimmed);
  return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
}

async function resolveDraw(
  filters: ReportFilters,
  messages: { drawNoRequired: string; drawNoNotFound: (drawNo: string) => string },
): Promise<{ id: number; draw_no: string }> {
  if (filters.drawId != null && filters.drawId > 0) {
    const drawNo = filters.drawNo.trim();
    return { id: filters.drawId, draw_no: drawNo || String(filters.drawId) };
  }

  const drawNo = filters.drawNo.trim();
  if (!drawNo) {
    throw new LotteryApiBizError(messages.drawNoRequired, -1, null);
  }

  const data = await getAdminDraws({ draw_no: drawNo, page: 1, per_page: 1 });
  const matched = data.items.find((item) => item.draw_no === drawNo) ?? data.items[0];
  if (!matched) {
    throw new LotteryApiBizError(messages.drawNoNotFound(drawNo), -1, { drawNo });
  }
  return { id: matched.id, draw_no: matched.draw_no };
}

function drawRowsFromSummary(summary: AdminDrawFinanceSummaryData): ExportRow[] {
  return [
    {
      row_type: "summary",
      draw_id: summary.draw_id,
      draw_no: summary.draw_no,
      draw_status: summary.draw_status,
      currency_code: summary.currency_code,
      order_count: summary.order_count,
      ticket_item_count: summary.ticket_item_count,
      total_bet_minor: summary.total_bet_minor,
      total_win_payout_minor: summary.total_win_payout_minor,
      total_jackpot_win_minor: summary.total_jackpot_win_minor,
      total_payout_minor: summary.total_payout_minor,
      approx_house_gross_minor: summary.approx_house_gross_minor,
    },
    ...summary.settlement_batches.map((batch) => ({
      row_type: "settlement_batch",
      draw_id: summary.draw_id,
      draw_no: summary.draw_no,
      settlement_batch_id: batch.id,
      settlement_status: batch.status,
      total_ticket_count: batch.total_ticket_count,
      total_win_count: batch.total_win_count,
      total_payout_amount: batch.total_payout_amount,
      total_jackpot_payout_amount: batch.total_jackpot_payout_amount,
      finished_at: formatExportInstant(batch.finished_at),
    })),
  ];
}

function resultRowCount(result: ReportResult | null): number {
  return result?.rows.length ?? 0;
}

function defaultSummaryCards(
  reportKey: ReportKey,
  filters: ReportFilters,
  t: (key: string) => string,
): StatCard[] {
  const periodLabel =
    filters.dateFrom && filters.dateTo
      ? `${filters.dateFrom} ~ ${filters.dateTo}`
      : filters.dateFrom || filters.dateTo || t("preview.stats.notQueried");

  switch (reportKey) {
    case "draw_profit":
      return [
        { label: t("preview.stats.bet"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.payout"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.houseGross"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.drawNo"), value: filters.drawNo || t("preview.stats.notSet") },
      ];
    case "daily_profit":
      return [
        { label: t("preview.stats.records"), value: t("preview.stats.notQueried") },
        { label: t("fields.period"), value: periodLabel },
        { label: t("preview.stats.bet"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.houseGross"), value: t("preview.stats.notQueried") },
      ];
    case "player_win_loss":
      return [
        { label: t("preview.stats.records"), value: t("preview.stats.notQueried") },
        { label: t("fields.player"), value: filters.player || t("preview.stats.notSet") },
        { label: t("preview.stats.players"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.houseGross"), value: t("preview.stats.notQueried") },
      ];
    case "player_transfer":
      return [
        { label: t("preview.stats.records"), value: t("preview.stats.notQueried") },
        { label: t("fields.player"), value: filters.player || t("preview.stats.notSet") },
        { label: t("preview.stats.transferIn"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.transferOut"), value: t("preview.stats.notQueried") },
      ];
    case "hot_number_risk":
      return [
        { label: t("preview.stats.drawNo"), value: filters.drawNo || t("preview.stats.notSet") },
        { label: t("fields.number"), value: filters.number || t("preview.stats.notSet") },
        { label: t("preview.stats.usage"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.logs"), value: t("preview.stats.notQueried") },
      ];
    case "play_dimension":
      return [
        { label: t("preview.stats.records"), value: t("preview.stats.notQueried") },
        { label: t("fields.play"), value: filters.play || t("filterAll") },
        { label: t("preview.stats.bet"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.payout"), value: t("preview.stats.notQueried") },
      ];
    case "sold_out_number":
      return [
        { label: t("preview.stats.records"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.drawNo"), value: filters.drawNo || t("preview.stats.notSet") },
        { label: t("preview.stats.currency"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.usage"), value: t("preview.stats.notQueried") },
      ];
    case "rebate_commission":
      return [
        { label: t("preview.stats.records"), value: t("preview.stats.notQueried") },
        { label: t("fields.play"), value: filters.play || t("filterAll") },
        { label: t("preview.stats.rebate"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.orders"), value: t("preview.stats.notQueried") },
      ];
    case "admin_audit":
      return [
        { label: t("preview.stats.records"), value: t("preview.stats.notQueried") },
        { label: t("fields.operator"), value: filters.operator || t("preview.stats.notSet") },
        { label: t("preview.stats.modules"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.operators"), value: t("preview.stats.notQueried") },
      ];
    default:
      return [
        { label: t("preview.stats.records"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.currentPage"), value: t("preview.stats.notQueried") },
        { label: t("preview.stats.exportRows"), value: "0" },
        { label: t("preview.stats.drawNo"), value: filters.drawNo || t("preview.stats.notSet") },
      ];
  }
}

export function ReportsConsole({ initialCategory }: { initialCategory?: ReportCategory } = {}) {
  const { t, i18n } = useTranslation(["reports", "common"]);
  const profile = useAdminProfile();
  const canViewReports = adminHasAnyPermission(profile?.permissions, [PRD_REPORT_VIEW]);
  const canExportReports = adminHasAnyPermission(profile?.permissions, [PRD_REPORT_EXPORT]);
  useAdminCurrencyCatalog();
  useAdminPlayTypeCatalog();
  const playCodeLabel = useAdminPlayCodeLabel();
  const formatTs = useAdminDateTimeFormatter();
  const filteredReports = useMemo(
    () => (initialCategory ? REPORTS.filter((report) => report.category === initialCategory) : REPORTS),
    [initialCategory],
  );
  const [selectedKey, setSelectedKey] = useState<ReportKey>(
    filteredReports[0]?.key ?? REPORTS[0].key,
  );
  const [filters, setFilters] = useState<ReportFilters>(emptyFilters);
  const [result, setResult] = useState<ReportResult | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [page, setPage] = useState(1);
  const [perPage, setPerPage] = useState(20);
  const [exporting, setExporting] = useState<ExportFormat | null>(null);
  const [search, setSearch] = useState<SearchState>(emptySearch);
  const playOptions = useCachedPlayTypeOptions();
  const tRef = useTranslationRef(["reports", "common"]);
  const searchParams = useSearchParams();
  const drawNoFromUrl = (searchParams.get("draw_no") ?? "").trim();

  const selectedReport = filteredReports.find((report) => report.key === selectedKey) ?? filteredReports[0] ?? REPORTS[0];

  useEffect(() => {
    if (!filteredReports.some((report) => report.key === selectedKey)) {
      setSelectedKey(filteredReports[0]?.key ?? REPORTS[0].key);
    }
  }, [filteredReports, selectedKey]);

  const pageScopedLabel = useCallback(
    (statKey: string) => `${t(`preview.stats.${statKey}`)} · ${t("preview.scope.currentPage")}`,
    [t],
  );

  const previewColumns = useMemo<PreviewColumns>(() => {
    switch (selectedReport.key) {
      case "draw_profit":
        return {
          primary: t("preview.columns.drawProfit.primary"),
          secondary: t("preview.columns.drawProfit.secondary"),
          metricA: t("preview.columns.drawProfit.metricA"),
          metricB: t("preview.columns.drawProfit.metricB"),
          metricC: t("preview.columns.drawProfit.metricC"),
          status: t("preview.columns.drawProfit.status"),
          extra: t("preview.columns.drawProfit.extra"),
          time: t("preview.columns.drawProfit.time"),
        };
      case "daily_profit":
        return {
          primary: t("preview.columns.dailyProfit.primary"),
          secondary: t("preview.columns.dailyProfit.secondary"),
          metricA: t("preview.columns.dailyProfit.metricA"),
          metricB: t("preview.columns.dailyProfit.metricB"),
          metricC: t("preview.columns.dailyProfit.metricC"),
          status: t("preview.columns.dailyProfit.status"),
          extra: t("preview.columns.dailyProfit.extra"),
          time: t("preview.columns.dailyProfit.time"),
        };
      case "player_win_loss":
        return {
          primary: t("preview.columns.playerWinLoss.primary"),
          secondary: t("agentColumns.agent", { ns: "common" }),
          metricA: t("preview.columns.playerWinLoss.metricA"),
          metricB: t("preview.columns.playerWinLoss.metricB"),
          metricC: t("preview.columns.playerWinLoss.metricC"),
          status: t("preview.columns.playerWinLoss.status"),
          extra: t("preview.columns.playerWinLoss.extra"),
          time: t("preview.columns.playerWinLoss.time"),
        };
      case "player_transfer":
        return {
          primary: t("preview.columns.playerTransfer.primary"),
          secondary: t("preview.columns.playerTransfer.secondary"),
          metricA: t("preview.columns.playerTransfer.metricA"),
          metricB: t("preview.columns.playerTransfer.metricB"),
          metricC: t("preview.columns.playerTransfer.metricC"),
          status: t("preview.columns.playerTransfer.status"),
          extra: t("preview.columns.playerTransfer.extra"),
          time: t("preview.columns.playerTransfer.time"),
        };
      case "hot_number_risk":
        return {
          primary: t("preview.columns.hotNumberRisk.primary"),
          secondary: t("preview.columns.hotNumberRisk.secondary"),
          metricA: t("preview.columns.hotNumberRisk.metricA"),
          metricB: t("preview.columns.hotNumberRisk.metricB"),
          metricC: t("preview.columns.hotNumberRisk.metricC"),
          status: t("preview.columns.hotNumberRisk.status"),
          extra: t("preview.columns.hotNumberRisk.extra"),
          time: t("preview.columns.hotNumberRisk.time"),
        };
      case "play_dimension":
        return {
          primary: t("preview.columns.playDimension.primary"),
          secondary: t("preview.columns.playDimension.secondary"),
          metricA: t("preview.columns.playDimension.metricA"),
          metricB: t("preview.columns.playDimension.metricB"),
          metricC: t("preview.columns.playDimension.metricC"),
          status: t("preview.columns.playDimension.status"),
          extra: t("preview.columns.playDimension.extra"),
          time: t("preview.columns.playDimension.time"),
        };
      case "sold_out_number":
        return {
          primary: t("preview.columns.soldOut.primary"),
          secondary: t("preview.columns.soldOut.secondary"),
          metricA: t("preview.columns.soldOut.metricA"),
          metricB: t("preview.columns.soldOut.metricB"),
          metricC: t("preview.columns.soldOut.metricC"),
          status: t("preview.columns.soldOut.status"),
          extra: t("preview.columns.soldOut.extra"),
          time: t("preview.columns.soldOut.time"),
        };
      case "rebate_commission":
        return {
          primary: t("preview.columns.rebateCommission.primary"),
          secondary: t("preview.columns.rebateCommission.secondary"),
          metricA: t("preview.columns.rebateCommission.metricA"),
          metricB: t("preview.columns.rebateCommission.metricB"),
          metricC: t("preview.columns.rebateCommission.metricC"),
          status: t("preview.columns.rebateCommission.status"),
          extra: t("preview.columns.rebateCommission.extra"),
          time: t("preview.columns.rebateCommission.time"),
        };
      case "admin_audit":
        return {
          primary: t("preview.columns.adminAudit.primary"),
          secondary: t("preview.columns.adminAudit.secondary"),
          metricA: t("preview.columns.adminAudit.metricA"),
          metricB: t("preview.columns.adminAudit.metricB"),
          metricC: t("preview.columns.adminAudit.metricC"),
          status: t("preview.columns.adminAudit.status"),
          extra: t("preview.columns.adminAudit.extra"),
          time: t("preview.columns.adminAudit.time"),
        };
      default:
        return {
          primary: t("preview.columns.primary"),
          secondary: t("preview.columns.secondary"),
          metricA: t("preview.columns.metricA"),
          metricB: t("preview.columns.metricB"),
          metricC: t("preview.columns.metricC"),
          status: t("preview.columns.status"),
          extra: t("preview.columns.extra"),
          time: t("preview.columns.time"),
        };
    }
  }, [selectedReport.key, t]);

  const exportFileBase = useMemo(() => {
    const segments: string[] = [selectedReport.key];
    if (filters.drawNo.trim()) segments.push(filters.drawNo.trim());
    if (filters.number.trim()) segments.push(filters.number.trim());
    if (filters.player.trim()) segments.push(filters.player.trim());
    if (filters.play.trim()) segments.push(filters.play.trim());
    if (filters.operator.trim()) segments.push(filters.operator.trim());
    if (filters.dateFrom) segments.push(filters.dateFrom);
    if (filters.dateTo) segments.push(filters.dateTo);
    return normalizeFilenamePart(segments.join("-")) || selectedReport.key;
  }, [selectedReport.key, filters]);

  const loadSearchOptions = useCallback(async (kind: SearchKind, query: string) => {
    setSearch((prev) => ({ ...prev, loading: true }));
    try {
      if (kind === "draw") {
        const payload = await getAdminDraws({ draw_no: query.trim() || undefined, page: 1, per_page: 8 });
        setSearch((prev) => ({ ...prev, draws: payload.items, loading: false }));
        return;
      }
      if (kind === "player") {
        const payload = await getAdminPlayers({ keyword: query.trim() || undefined, page: 1, per_page: 8 });
        setSearch((prev) => ({ ...prev, players: payload.items, loading: false }));
        return;
      }
      const payload = await getAdminUsers({ keyword: query.trim() || undefined, page: 1, per_page: 8 });
      setSearch((prev) => ({ ...prev, operators: payload.items, loading: false }));
    } catch {
      setSearch((prev) => ({ ...prev, loading: false }));
    }
  }, []);

  useEffect(() => {
    if (search.open === null) {
      return;
    }
    const timer = window.setTimeout(() => {
      void loadSearchOptions(search.open as SearchKind, search.query);
    }, 250);
    return () => window.clearTimeout(timer);
  }, [search.open, search.query, loadSearchOptions]);

  const queryReport = useCallback(async () => {
    if (!canViewReports) {
      return;
    }
    setLoading(true);
    setError(null);
    try {
      switch (selectedReport.key) {
        case "draw_profit": {
          const draw = await resolveDraw(filters, {
            drawNoRequired: tRef.current("validation.drawNoRequired", { ns: "reports" }),
            drawNoNotFound: (drawNo) =>
              tRef.current("validation.drawNoNotFound", { ns: "reports", drawNo }),
          });
          const summary = await getAdminDrawFinanceSummary(draw.id);
          setResult({
            key: "draw_profit",
            raw: summary,
            rows: drawRowsFromSummary(summary),
            meta: null,
            summary: [
              { label: t("preview.stats.bet"), value: formatPlainMoney(summary.total_bet_minor, summary.currency_code) },
              { label: t("preview.stats.payout"), value: formatPlainMoney(summary.total_payout_minor, summary.currency_code) },
              {
                label: t("preview.stats.houseGross"),
                value: formatPlainMoney(summary.approx_house_gross_minor, summary.currency_code),
                tone: summary.approx_house_gross_minor >= 0 ? "good" : "bad",
              },
              { label: t("preview.stats.orders"), value: String(summary.order_count) },
            ],
          });
          break;
        }
        case "daily_profit": {
          const payload = await getAdminReportDailyProfit(
            reportListParams(filters, page, perPage),
          );
          const next = buildDailyProfitRowsAndSummary(payload.items, payload.meta.total, t, pageScopedLabel);
          setResult({
            key: "daily_profit",
            raw: payload.items,
            rows: next.rows,
            meta: metaFromList(payload.meta),
            summary: next.summary,
          });
          break;
        }
        case "player_win_loss": {
          const payload = await getAdminReportPlayerWinLoss(
            reportListParams(filters, page, perPage),
          );
          const rows = payload.items.map((item) => ({
            player_id: item.player_id,
            username: item.username,
            total_bet_minor: item.total_bet_minor,
            total_payout_minor: item.total_payout_minor,
            net_win_loss_minor: item.net_win_loss_minor,
          }));
          setResult({
            key: "player_win_loss",
            raw: payload.items,
            rows,
            meta: metaFromList(payload.meta),
            summary: [
              { label: t("preview.stats.records"), value: String(payload.meta.total) },
              { label: t("preview.stats.currentPage"), value: String(payload.items.length) },
              {
                label: pageScopedLabel("houseGross"),
                value: formatPlainMoney(
                  payload.items.reduce((sum, item) => sum - item.net_win_loss_minor, 0),
                  "NPR",
                ),
                tone: (() => {
                  const houseGross = payload.items.reduce((sum, item) => sum - item.net_win_loss_minor, 0);
                  return houseGross >= 0 ? "good" : "bad";
                })(),
              },
              { label: t("preview.stats.players"), value: String(new Set(payload.items.map((item) => item.player_id)).size) },
            ],
          });
          break;
        }
        case "player_transfer": {
          const playerId = filters.playerId ?? parsePositiveInteger(filters.player);
          const payload = await getAdminTransferOrders({
            page,
            per_page: perPage,
            player_id: playerId ?? undefined,
            player_account: playerId ? undefined : filters.player.trim() || undefined,
            created_from: filters.dateFrom || undefined,
            created_to: filters.dateTo || undefined,
          });
          const next = buildPlayerTransferRowsAndSummary(
            payload.items,
            payload.total,
            payload.page,
            payload.per_page,
            t,
            pageScopedLabel,
          );
          setResult({
            key: "player_transfer",
            raw: payload.items,
            rows: next.rows,
            meta: next.meta,
            summary: next.summary,
          });
          break;
        }
        case "hot_number_risk": {
          if (!filters.number.trim()) {
            throw new LotteryApiBizError(tRef.current("validation.drawNoNumberRequired"), -1, null);
          }
          const draw = await resolveDraw(filters, {
            drawNoRequired: tRef.current("validation.drawNoRequired", { ns: "reports" }),
            drawNoNotFound: (drawNo) =>
              tRef.current("validation.drawNoNotFound", { ns: "reports", drawNo }),
          });
          const detail = await getAdminRiskPoolDetail(draw.id, filters.number.trim(), { page, per_page: perPage });
          const rows: ExportRow[] = [
            {
              row_type: "risk_pool",
              draw_id: detail.draw_id,
              draw_no: detail.draw_no,
              number: detail.pool.normalized_number,
              total_cap_amount: detail.pool.total_cap_amount,
              locked_amount: detail.pool.locked_amount,
              remaining_amount: detail.pool.remaining_amount,
              sold_out_status: detail.pool.sold_out_status,
              is_sold_out: detail.pool.is_sold_out ? 1 : 0,
              usage_ratio: detail.pool.usage_ratio,
              version: detail.pool.version,
            },
            ...detail.logs.items.map((item) => ({
              row_type: "lock_log",
              draw_id: detail.draw_id,
              draw_no: detail.draw_no,
              number: detail.pool.normalized_number,
              log_id: item.id,
              action_type: item.action_type,
              amount: item.amount,
              source_reason: item.source_reason,
              ticket_item_id: item.ticket_item_id,
              ticket_no: item.ticket_no,
              play_code: item.play_code,
              player_id: item.player_id,
              created_at: formatExportInstant(item.created_at),
            })),
          ];
          setResult({
            key: "hot_number_risk",
            raw: detail,
            rows,
            meta: metaFromList(detail.logs.meta),
            summary: [
              { label: t("preview.stats.locked"), value: formatPlainMoney(detail.pool.locked_amount, detail.currency_code) },
              { label: t("preview.stats.remaining"), value: formatPlainMoney(detail.pool.remaining_amount, detail.currency_code), tone: detail.pool.is_sold_out ? "bad" : "good" },
              { label: t("preview.stats.usage"), value: formatUsagePercent(detail.pool.usage_ratio), tone: detail.pool.is_sold_out ? "bad" : "warn" },
              { label: t("preview.stats.logs"), value: String(detail.logs.meta.total) },
            ],
          });
          break;
        }
        case "sold_out_number": {
          const draw = await resolveDraw(filters, {
            drawNoRequired: tRef.current("validation.drawNoRequired", { ns: "reports" }),
            drawNoNotFound: (drawNo) =>
              tRef.current("validation.drawNoNotFound", { ns: "reports", drawNo }),
          });
          const payload = await getAdminRiskPools(draw.id, { page, per_page: perPage, sold_out_only: true, sort: "number_asc" });
          const rows = payload.items.map((item) => ({
            draw_id: payload.draw_id,
            draw_no: payload.draw_no,
            currency_code: payload.currency_code,
            normalized_number: item.normalized_number,
            total_cap_amount: item.total_cap_amount,
            locked_amount: item.locked_amount,
            remaining_amount: item.remaining_amount,
            sold_out_status: item.sold_out_status,
            is_sold_out: item.is_sold_out ? 1 : 0,
            usage_ratio: item.usage_ratio,
            version: item.version,
          }));
          setResult({
            key: "sold_out_number",
            raw: payload.items,
            rows,
            meta: metaFromList(payload.meta),
            summary: [
              { label: t("preview.stats.records"), value: String(payload.meta.total), tone: payload.meta.total > 0 ? "bad" : "good" },
              { label: t("preview.stats.currentPage"), value: String(payload.items.length) },
              { label: t("preview.stats.drawNo"), value: payload.draw_no },
              { label: t("preview.stats.currency"), value: payload.currency_code || "-" },
            ],
          });
          break;
        }
        case "play_dimension": {
          const payload = await getAdminReportPlayDimension(
            reportListParams(filters, page, perPage),
          );
          const next = buildPlayDimensionRowsAndSummary(payload.items, payload.meta.total, t, pageScopedLabel);
          setResult({
            key: "play_dimension",
            raw: payload.items,
            rows: next.rows,
            meta: metaFromList(payload.meta),
            summary: next.summary,
          });
          break;
        }
        case "rebate_commission": {
          const payload = await getAdminReportRebateCommission(
            reportListParams(filters, page, perPage),
          );
          const next = buildRebateCommissionRowsAndSummary(payload.items, payload.meta.total, t, pageScopedLabel);
          setResult({
            key: "rebate_commission",
            raw: payload.items,
            rows: next.rows,
            meta: metaFromList(payload.meta),
            summary: next.summary,
          });
          break;
        }
        case "admin_audit": {
          const operatorId = filters.operatorId ?? parsePositiveInteger(filters.operator);
          const payload = await getAdminAuditLogs({
            page,
            per_page: perPage,
            operator_id: operatorId ?? undefined,
            operator_type: operatorId ? undefined : filters.operator.trim() || undefined,
            start_date: filters.dateFrom || undefined,
            end_date: filters.dateTo || undefined,
          });
          const rows = payload.items.map((item) => ({
            id: item.id,
            operator_type: item.operator_type,
            operator_id: item.operator_id,
            module_code: item.module_code,
            action_code: item.action_code,
            target_type: item.target_type,
            target_id: item.target_id,
            ip: item.ip,
            user_agent: item.user_agent,
            created_at: formatExportInstant(item.created_at),
          }));
          setResult({
            key: "admin_audit",
            raw: payload.items,
            rows,
            meta: metaFromList(payload.meta),
            summary: [
              { label: t("preview.stats.records"), value: String(payload.meta.total) },
              { label: t("preview.stats.currentPage"), value: String(payload.items.length) },
              { label: t("preview.stats.modules"), value: String(new Set(payload.items.map((item) => item.module_code)).size) },
              { label: t("preview.stats.operators"), value: String(new Set(payload.items.map((item) => item.operator_id)).size) },
            ],
          });
          break;
        }
        default:
          setResult(null);
          setError(tRef.current("loadFailed"));
      }
    } catch (err) {
      setResult(null);
      setError(err instanceof LotteryApiBizError ? err.message : tRef.current("loadFailed"));
    } finally {
      setLoading(false);
    }
  }, [canViewReports, filters, page, perPage, selectedReport]);

  useEffect(() => {
    queueMicrotask(() => {
      setResult(null);
      setError(null);
      setPage(1);
    });
  }, [selectedKey]);

  useEffect(() => {
    setFilters((prev) => ({
      ...prev,
      drawNo: drawNoFromUrl || prev.drawNo,
    }));
    if (drawNoFromUrl) {
      setSelectedKey("draw_profit");
    }
  }, [drawNoFromUrl]);

  useEffect(() => {
    queueMicrotask(() => {
      setResult(null);
      setError(null);
      setPage(1);
    });
  }, []);

  useEffect(() => {
    if (result && result.key === selectedReport.key && selectedReport.connected) {
      queueMicrotask(() => {
        void queryReport();
      });
    }
  }, [page, perPage]);

  function updateFilter<K extends keyof ReportFilters>(key: K, value: ReportFilters[K]): void {
    setFilters((prev) => ({ ...prev, [key]: value }));
  }

  function resetFilters(): void {
    setFilters(emptyFilters);
    setResult(null);
    setError(null);
    setPage(1);
  }

  const usesServerExport = REPORT_UI_SERVER_FULL_EXPORT.has(selectedReport.key as ReportUiKey);

  async function exportViaServer(format: ExportFormat): Promise<void> {
    if (!canExportReports) {
      return;
    }
    setExporting(format);
    try {
      const parameters = buildReportJobParameters(selectedReport.key as ReportUiKey, {
        dateFrom: filters.dateFrom,
        dateTo: filters.dateTo,
        playerId: filters.playerId,
        play: filters.play,
        operatorId: filters.operatorId,
        drawId: filters.drawId,
        drawNo: filters.drawNo,
        number: filters.number,
      });
      const job = await postAdminReportJob({
        report_type: REPORT_UI_TO_JOB_TYPE[selectedReport.key as ReportUiKey],
        export_format: format === "excel" ? "xlsx" : "csv",
        parameters,
      });
      const { blob, filename } = await downloadAdminReportJob(job.id);
      const ext = job.export_format === "xlsx" ? "xlsx" : "csv";
      downloadBlob(blob, filename ?? `${exportFileBase}.${ext}`);
      toast.success(
        t("exportServerSuccess", {
          report: t(`items.${selectedReport.key}.title`),
          format: t(`formats.${format}`),
          jobNo: job.job_no,
        }),
      );
    } catch (err) {
      toast.error(err instanceof LotteryApiBizError ? err.message : t("exportFailed"));
    } finally {
      setExporting(null);
    }
  }

  function exportPreview(format: ExportFormat): void {
    if (!canExportReports) {
      return;
    }
    if (!result || result.rows.length === 0) {
      toast.info(t("empty"));
      return;
    }
    setExporting(format);
    try {
      exportRows(result.rows, exportFileBase, t(`items.${selectedReport.key}.title`), format);
      toast.success(t("exportSuccess", { report: t(`items.${selectedReport.key}.title`), format: t(`formats.${format}`) }));
    } catch (err) {
      toast.error(err instanceof LotteryApiBizError ? err.message : t("exportFailed"));
    } finally {
      setExporting(null);
    }
  }

  function exportReport(format: ExportFormat): void {
    if (!canExportReports) {
      return;
    }
    if (usesServerExport) {
      void exportViaServer(format);
      return;
    }
    exportPreview(format);
  }

  const renderSearchPicker = (kind: SearchKind) => {
    const value =
      kind === "draw" ? filters.drawNo : kind === "player" ? filters.player : filters.operator;
    const labelKey = kind === "draw" ? "drawNo" : kind === "player" ? "player" : "operator";
    const open = search.open === kind;

    return (
      <div className="grid gap-1.5">
        <Label htmlFor={`report-${kind}`}>{t(`fields.${labelKey}`)}</Label>
        <Popover
          open={open}
          onOpenChange={(nextOpen) => {
            setSearch((prev) =>
              nextOpen
                ? {
                    ...prev,
                    open: kind,
                    query: value,
                  }
                : emptySearch,
            );
          }}
          modal={false}
        >
          <div className="flex gap-2">
          <Input
            id={`report-${kind}`}
            value={value}
            onChange={(e) => {
              const next = e.target.value;
              if (kind === "draw") {
                setFilters((prev) => ({ ...prev, drawNo: next, drawId: null }));
              } else if (kind === "player") {
                setFilters((prev) => ({ ...prev, player: next, playerId: null }));
              } else {
                setFilters((prev) => ({ ...prev, operator: next, operatorId: null }));
              }
            }}
            placeholder={t(`placeholders.${labelKey}`)}
          />
            <PopoverTrigger render={<Button type="button" variant="outline" className="shrink-0" aria-label={t("searchPicker.open")} />}>
              <Search data-icon="inline-start" />
              {t("searchPicker.select")}
            </PopoverTrigger>
          </div>
          <PopoverContent
            align="start"
            side="bottom"
            sideOffset={6}
            className="z-[80] w-[var(--anchor-width)] min-w-[min(24rem,calc(100vw-2rem))] max-w-[calc(100vw-2rem)] p-2"
          >
            <Input
              value={search.query}
              placeholder={t("searchPicker.keyword")}
              onChange={(e) => setSearch((prev) => ({ ...prev, query: e.target.value }))}
            />
            <div className="mt-2 max-h-64 overflow-auto">
              {search.loading ? (
                <AdminLoadingInline className="py-2" />
              ) : null}
              {!search.loading && kind === "draw" ? (
                search.draws.map((item) => (
                  <button
                    key={item.id}
                    type="button"
                    className="flex w-full items-center justify-between rounded-md px-2 py-2 text-left text-sm hover:bg-muted"
                    onClick={() => {
                      setFilters((prev) => ({ ...prev, drawNo: item.draw_no, drawId: item.id }));
                      setSearch(emptySearch);
                    }}
                  >
                    <span className="font-medium">{item.draw_no}</span>
                    <span className="text-xs text-muted-foreground">{item.status}</span>
                  </button>
                ))
              ) : null}
              {!search.loading && kind === "player" ? (
                search.players.map((item) => (
                  <button
                    key={item.id}
                    type="button"
                    className="flex w-full items-center justify-between gap-3 rounded-md px-2 py-2 text-left text-sm hover:bg-muted"
                    onClick={() => {
                      setFilters((prev) => ({
                        ...prev,
                        player: optionText(item.username, item.site_player_id, `ID ${item.id}`),
                        playerId: item.id,
                      }));
                      setSearch(emptySearch);
                    }}
                  >
                    <span className="min-w-0 truncate font-medium">{optionText(item.username, item.nickname, item.site_player_id)}</span>
                    <span className="shrink-0 text-xs text-muted-foreground">ID {item.id}</span>
                  </button>
                ))
              ) : null}
              {!search.loading && kind === "operator" ? (
                search.operators.map((item) => (
                  <button
                    key={item.id}
                    type="button"
                    className="flex w-full items-center justify-between gap-3 rounded-md px-2 py-2 text-left text-sm hover:bg-muted"
                    onClick={() => {
                      setFilters((prev) => ({
                        ...prev,
                        operator: optionText(item.username, item.nickname, `ID ${item.id}`),
                        operatorId: item.id,
                      }));
                      setSearch(emptySearch);
                    }}
                  >
                    <span className="min-w-0 truncate font-medium">{optionText(item.username, item.nickname)}</span>
                    <span className="shrink-0 text-xs text-muted-foreground">ID {item.id}</span>
                  </button>
                ))
              ) : null}
            </div>
          </PopoverContent>
        </Popover>
      </div>
    );
  };

  const renderField = (field: FieldKey) => {
    if (field === "period") {
      return (
        <AdminDateRangeField
          key={field}
          id="report-date-range"
          label={t("fields.period")}
          from={filters.dateFrom}
          to={filters.dateTo}
          onRangeChange={({ from, to }) => {
            setFilters((prev) => ({ ...prev, dateFrom: from, dateTo: to }));
          }}
        />
      );
    }
    if (field === "drawNo") {
      return <div key={field}>{renderSearchPicker("draw")}</div>;
    }
    if (field === "player") {
      return <div key={field}>{renderSearchPicker("player")}</div>;
    }
    if (field === "operator") {
      return <div key={field}>{renderSearchPicker("operator")}</div>;
    }
    if (field === "play") {
      return (
        <div key={field} className="grid gap-1.5">
          <Label htmlFor="report-play">{t("fields.play")}</Label>
          <Select
            modal={false}
            value={filters.play || "__none__"}
            onValueChange={(value) => updateFilter("play", value === "__none__" ? "" : String(value))}
          >
            <SelectTrigger id="report-play" className="h-8 w-full">
              <SelectValue>{filters.play ? playOptions.find((item) => item.code === filters.play)?.label ?? filters.play : t("placeholders.play")}</SelectValue>
            </SelectTrigger>
            <SelectContent align="start" sideOffset={6}>
              <SelectItem value="__none__">{t("filterAll")}</SelectItem>
              {playOptions.map((item) => (
                <SelectItem key={item.code} value={item.code}>
                  {item.label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
      );
    }

    return (
      <div key={field} className="grid gap-1.5">
        <Label htmlFor={`report-${field}`}>{t(`fields.${field}`)}</Label>
        <Input
          id={`report-${field}`}
          value={filters.number}
          onChange={(e) => updateFilter("number", e.target.value)}
          placeholder={t(`placeholders.${field}`)}
        />
      </div>
    );
  };

  const renderTable = () => {
    if (!selectedReport.connected) {
      return (
        <TableRow>
          <TableCell colSpan={8} className="text-muted-foreground">
            {t("backendPending")}
          </TableCell>
        </TableRow>
      );
    }
    if (loading) {
      return (
        <AdminTableLoadingRow colSpan={8} />
      );
    }
    if (error) {
      return (
        <TableRow>
          <TableCell colSpan={8} className="text-destructive">
            {error}
          </TableCell>
        </TableRow>
      );
    }
    if (!result || result.rows.length === 0) {
      return (
        <AdminTableNoResourceRow colSpan={8} />
      );
    }

    if (result.key === "draw_profit") {
      const summary = result.raw;
      return (
        <>
          <TableRow>
            <TableCell className="font-medium">{summary.draw_no}</TableCell>
            <TableCell>{summary.draw_status}</TableCell>
            <TableCell className="text-center">{summary.order_count}</TableCell>
            <TableCell className="text-center">{summary.ticket_item_count}</TableCell>
            <TableCell className="text-center">{formatPlainMoney(summary.total_bet_minor, summary.currency_code)}</TableCell>
            <TableCell className="text-center">{formatPlainMoney(summary.total_payout_minor, summary.currency_code)}</TableCell>
            <TableCell className={signedProfitCell(summary.approx_house_gross_minor, summary.currency_code)}>
              {formatPlainMoney(summary.approx_house_gross_minor, summary.currency_code)}
            </TableCell>
            <TableCell>{summary.settlement_batches.length}</TableCell>
          </TableRow>
          {summary.settlement_batches.map((batch) => (
            <TableRow key={batch.id} className="bg-muted/15">
              <TableCell>#{batch.id}</TableCell>
              <TableCell>{batch.status}</TableCell>
              <TableCell className="text-center">{batch.total_ticket_count}</TableCell>
              <TableCell className="text-center">{batch.total_win_count}</TableCell>
              <TableCell className="text-center">-</TableCell>
              <TableCell className="text-center">{formatPlainMoney(batch.total_payout_amount, summary.currency_code)}</TableCell>
              <TableCell className="text-center">{formatPlainMoney(batch.total_jackpot_payout_amount, summary.currency_code)}</TableCell>
              <TableCell>{formatTs(batch.finished_at)}</TableCell>
            </TableRow>
          ))}
        </>
      );
    }

    if (result.key === "player_transfer") {
      return result.raw.map((item) => (
        <TableRow key={item.id}>
          <TableCell className="font-mono text-xs">{item.transfer_no}</TableCell>
          <TableCell>{optionText(item.username, item.nickname) || item.player_id}</TableCell>
          <TableCell>{item.direction}</TableCell>
          <TableCell>{item.status}</TableCell>
          <TableCell className="text-center">{item.currency_code} {item.amount}</TableCell>
          <TableCell>{item.external_ref_no || "-"}</TableCell>
          <TableCell>{item.fail_reason || "-"}</TableCell>
          <TableCell>{formatTs(item.created_at)}</TableCell>
        </TableRow>
      ));
    }

    if (result.key === "hot_number_risk") {
      return (
        <>
          <TableRow>
            <TableCell className="font-medium">{result.raw.pool.normalized_number}</TableCell>
            <TableCell>{result.raw.draw_no}</TableCell>
            <TableCell className="text-center">{formatPlainMoney(result.raw.pool.total_cap_amount, result.raw.currency_code)}</TableCell>
            <TableCell className="text-center">{formatPlainMoney(result.raw.pool.locked_amount, result.raw.currency_code)}</TableCell>
            <TableCell className="text-center">{formatPlainMoney(result.raw.pool.remaining_amount, result.raw.currency_code)}</TableCell>
            <TableCell>{result.raw.pool.is_sold_out ? t("yes") : t("no")}</TableCell>
            <TableCell>{formatUsagePercent(result.raw.pool.usage_ratio)}</TableCell>
            <TableCell>v{result.raw.pool.version}</TableCell>
          </TableRow>
          {result.raw.logs.items.map((item) => (
            <TableRow key={item.id} className="bg-muted/15">
              <TableCell className="font-mono text-xs">#{item.id}</TableCell>
              <TableCell>{item.action_type}</TableCell>
              <TableCell className="text-center">{formatPlainMoney(item.amount, result.raw.currency_code)}</TableCell>
              <TableCell>{playCodeLabel(item.play_code)}</TableCell>
              <TableCell>{item.ticket_no || "-"}</TableCell>
              <TableCell>{item.player_id || "-"}</TableCell>
              <TableCell>{item.source_reason || "-"}</TableCell>
              <TableCell>{formatTs(item.created_at)}</TableCell>
            </TableRow>
          ))}
        </>
      );
    }

    if (result.key === "sold_out_number") {
      return result.raw.map((item) => (
        <TableRow key={item.normalized_number}>
          <TableCell className="font-medium">{item.normalized_number}</TableCell>
          <TableCell>{filters.drawNo}</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.total_cap_amount, null)}</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.locked_amount, null)}</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.remaining_amount, null)}</TableCell>
          <TableCell>{item.is_sold_out ? t("yes") : t("no")}</TableCell>
          <TableCell>{formatUsagePercent(item.usage_ratio)}</TableCell>
          <TableCell>v{item.version}</TableCell>
        </TableRow>
      ));
    }

    if (result.key === "daily_profit") {
      return result.raw.map((item) => (
        <TableRow key={item.business_date}>
          <TableCell className="font-medium">{item.business_date}</TableCell>
          <TableCell>-</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.total_bet_minor, "NPR")}</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.total_payout_minor, "NPR")}</TableCell>
          <TableCell className={signedProfitCell(item.approx_house_gross_minor, "NPR")}>
            {formatPlainMoney(item.approx_house_gross_minor, "NPR")}
          </TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
        </TableRow>
      ));
    }

    if (result.key === "player_win_loss") {
      return result.raw.map((item) => (
        <TableRow key={item.player_id}>
          <TableCell className="font-medium">{item.username}</TableCell>
          <TableCell className="text-xs">
            {adminAgentDisplayLabel(item)}
            <span className="mt-0.5 block text-muted-foreground">ID {item.player_id}</span>
          </TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.total_bet_minor, "NPR")}</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.total_payout_minor, "NPR")}</TableCell>
          <TableCell className={signedProfitCell(item.net_win_loss_minor, "NPR")}>
            {formatPlainMoney(item.net_win_loss_minor, "NPR")}
          </TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
        </TableRow>
      ));
    }

    if (result.key === "play_dimension") {
      return result.raw.map((item) => (
        <TableRow key={`${item.play_code}-${item.dimension}`}>
          <TableCell className="font-medium">{playCodeLabel(item.play_code)}</TableCell>
          <TableCell>{item.dimension}D</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.total_bet_minor, "NPR")}</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.total_payout_minor, "NPR")}</TableCell>
          <TableCell className={signedProfitCell(item.approx_house_gross_minor, "NPR")}>
            {formatPlainMoney(item.approx_house_gross_minor, "NPR")}
          </TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
        </TableRow>
      ));
    }

    if (result.key === "rebate_commission") {
      return result.raw.map((item) => (
        <TableRow key={item.play_code}>
          <TableCell className="font-medium">{playCodeLabel(item.play_code)}</TableCell>
          <TableCell>{item.order_count}</TableCell>
          <TableCell className="text-center">{formatPlainMoney(item.total_rebate_minor, "NPR")}</TableCell>
          <TableCell className="text-center">{item.ticket_item_count}</TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
          <TableCell>-</TableCell>
        </TableRow>
      ));
    }

    if (result.key === "admin_audit") {
      return result.raw.map((item) => (
        <TableRow key={item.id}>
          <TableCell className="font-mono text-xs">#{item.id}</TableCell>
          <TableCell>{item.operator_type}</TableCell>
          <TableCell>{item.operator_id}</TableCell>
          <TableCell>{item.module_code}</TableCell>
          <TableCell>{item.action_code}</TableCell>
          <TableCell>{item.target_type || "-"}</TableCell>
          <TableCell>{item.ip || "-"}</TableCell>
          <TableCell>{formatTs(item.created_at)}</TableCell>
        </TableRow>
      ));
    }

    return null;
  };

  return (
    <div className="mx-auto flex w-full max-w-7xl flex-col gap-4">
      <Card className="admin-list-card">
        <CardHeader className="admin-list-header pb-3">
          <div className="flex flex-col gap-3">
            <div className="flex flex-wrap gap-2">
              {filteredReports.map((report) => {
                const Icon = report.icon;
                const active = report.key === selectedReport.key;
                return (
                  <button
                    key={report.key}
                    type="button"
                    onClick={() => setSelectedKey(report.key)}
                    className={cn(
                      "inline-flex min-w-0 items-center gap-2 rounded-md border px-2.5 py-1.5 text-sm transition",
                      active
                        ? "border-primary bg-primary/[0.06] text-primary shadow-sm"
                        : "border-border/80 bg-card text-muted-foreground hover:border-primary/35 hover:text-foreground",
                    )}
                  >
                    <span className={cn("flex size-6 shrink-0 items-center justify-center rounded-md border", categoryTone(report.category))}>
                      <Icon className="size-3.5" aria-hidden />
                    </span>
                    <span className="truncate">{t(`items.${report.key}.title`)}</span>
                  </button>
                );
              })}
            </div>
            <div className="text-sm text-muted-foreground">{t(`items.${selectedReport.key}.summary`)}</div>
          </div>
        </CardHeader>
        <CardContent className="space-y-3 pt-0">
          <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
            {selectedReport.fields.map(renderField)}
          </div>
          <div className="flex flex-col gap-2 border-t border-border/60 pt-3 sm:flex-row sm:items-center sm:justify-between">
            <div className="text-xs text-muted-foreground">{t("filterPanel")}</div>
            <div className="flex shrink-0 gap-2">
              <Button type="button" variant="outline" size="sm" onClick={resetFilters}>
                {t("reset")}
              </Button>
              <Button
                type="button"
                size="sm"
                disabled={!canViewReports || !selectedReport.connected || loading}
                onClick={() => {
                  setPage(1);
                  void queryReport();
                }}
              >
                <Database data-icon="inline-start" />
                {loading ? t("querying") : t("query")}
              </Button>
            </div>
          </div>
        </CardContent>
      </Card>

      <div className="grid gap-2 md:grid-cols-4">
        {(result?.summary ?? defaultSummaryCards(selectedReport.key, filters, t)).map((item) => (
          <div key={item.label} className={cn("rounded-md border px-3 py-2.5", statTone(item.tone))}>
            <div className="text-xs text-muted-foreground">{item.label}</div>
            <div className="mt-0.5 truncate text-base font-semibold tabular-nums">{item.value}</div>
          </div>
        ))}
      </div>

      {selectedReport.key === "rebate_commission" ? (
        <div className="rounded-md border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-950">
          {t("items.rebate_commission.disclaimer", {
            defaultValue:
              "本报表为钱包盘「下注立减回水/佣金」口径，不属于信用占成盘账期结算。占成盘请使用「代理 → 代理账单」中的账期报表。",
          })}
        </div>
      ) : null}

      <Card className="admin-list-card">
        <CardHeader className="admin-list-header flex flex-col gap-2 pb-3 sm:flex-row sm:items-center sm:justify-between">
          <div>
            <CardTitle className="admin-list-title">{t("preview.title")}</CardTitle>
          </div>
          <div className="flex flex-col items-end gap-1.5">
            <div className="flex flex-wrap justify-end gap-2">
              <Button
                type="button"
                variant="outline"
                size="sm"
                disabled={!canExportReports || exporting !== null}
                onClick={() => exportReport("csv")}
              >
                <FileDown data-icon="inline-start" />
                {t("formats.csvServer")}
              </Button>
              <Button
                type="button"
                size="sm"
                disabled={!canExportReports || exporting !== null}
                onClick={() => exportReport("excel")}
              >
                <FileSpreadsheet data-icon="inline-start" />
                {t("formats.excelServer")}
              </Button>
            </div>
            {result && result.rows.length > 0 ? (
              <>
                    <p className="text-xs text-muted-foreground">{t("exportPreviewHint")}</p>
                    <div className="flex flex-wrap justify-end gap-2">
                      <Button
                        type="button"
                        variant="ghost"
                    size="sm"
                    disabled={!canExportReports || exporting !== null}
                    onClick={() => exportPreview("csv")}
                  >
                    {t("formats.csvPreview")}
                  </Button>
                  <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    disabled={!canExportReports || exporting !== null}
                    onClick={() => exportPreview("excel")}
                  >
                    {t("formats.excelPreview")}
                  </Button>
                </div>
              </>
                ) : null}
              </div>
            </CardHeader>
        <CardContent className="space-y-3 pt-3">
          <div className="rounded-md border border-amber-200 bg-amber-50/70 px-3 py-2 text-xs text-amber-950">
            {t("preview.summaryScopeHint")}
          </div>
          <Table id="reports-preview-table">
            <TableHeader>
              <TableRow>
                <TableHead>{previewColumns.primary}</TableHead>
                <TableHead>{previewColumns.secondary}</TableHead>
                <TableHead className="text-center">{previewColumns.metricA}</TableHead>
                <TableHead className="text-center">{previewColumns.metricB}</TableHead>
                <TableHead className="text-center">{previewColumns.metricC}</TableHead>
                <TableHead>{previewColumns.status}</TableHead>
                <TableHead>{previewColumns.extra}</TableHead>
                <TableHead>{previewColumns.time}</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>{renderTable()}</TableBody>
          </Table>

          {result?.meta ? (
            <AdminListPaginationFooter
              selectId="reports-preview-per-page"
              total={result.meta.total}
              page={result.meta.page}
              lastPage={result.meta.lastPage}
              perPage={result.meta.perPage}
              loading={loading}
              onPerPageChange={(next) => {
                setPerPage(next);
                setPage(1);
              }}
              onPageChange={setPage}
            />
          ) : null}
        </CardContent>
      </Card>
    </div>
  );
}
