import { getAdminDraws } from "@/api/admin-draws";
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 { getCachedAdminCurrencies } from "@/hooks/use-admin-currency-catalog";
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,
} from "@/types/api/admin-reports";

import type { ReportDefinition, ReportKey } from "@/modules/reports/reports-definitions";

export type ExportCell = string | number | null;
export type ExportRow = Record<string, ExportCell>;
export type ExportFormat = "csv" | "excel";
export type SearchKind = "draw" | "player" | "operator";

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

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

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

export 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: "admin_audit"; rows: ExportRow[]; summary: StatCard[]; meta: ReportMeta; raw: AdminAuditLogRow[] };

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

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

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

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

function defaultReportPeriod(): Pick<ReportFilters, "dateFrom" | "dateTo"> {
  const to = new Date();
  const from = new Date();
  from.setDate(from.getDate() - 29);
  return { dateFrom: isoDateLocal(from), dateTo: isoDateLocal(to) };
}

export function createDefaultFilters(): ReportFilters {
  return { ...emptyFilters, ...defaultReportPeriod() };
}

export function reportHasPeriodField(report: ReportDefinition): boolean {
  return report.fields.includes("period");
}

export function resolveDisplayCurrency(apiCode?: string | null): string {
  const trimmed = apiCode?.trim();
  if (trimmed) {
    return trimmed;
  }
  const fallback = getCachedAdminCurrencies().find((row) => row.is_enabled && row.is_bettable)?.code;
  return fallback?.trim() || "NPR";
}

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

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function signedProfitCell(amount: number, _currencyCode: string | null | undefined): string {
  return cn("tabular-nums", signedMoneyClass(amount, true));
}

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

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

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

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

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

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

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

export 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),
    })),
  ];
}

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

  const rows = items.map((item) => {
    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("houseGross"),
        value: formatPlainMoney(totalGross, currencyCode),
        tone: totalGross >= 0 ? "good" : "bad",
      },
    ],
  };
}

export function buildPlayerTransferRowsAndSummary(
  items: AdminTransferOrderItem[],
  total: number,
  page: number,
  perPage: number,
  t: (key: string) => string,
): Pick<Extract<ReportResult, { key: "player_transfer" }>, "rows" | "summary" | "meta"> {
  const rows = items.map((item) => ({
    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) },
    ],
  };
}

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

  const rows = items.map((item) => {
    totalGross += item.approx_house_gross_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.houseGross"),
        value: formatPlainMoney(totalGross, currencyCode),
        tone: totalGross >= 0 ? "good" : "bad",
      },
    ],
  };
}

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

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

/** Primary headline stats shown above the preview table (max 2). */
export function headlineStats(result: ReportResult | null, reportKey: ReportKey): StatCard[] {
  if (!result || result.key !== reportKey) {
    return [];
  }
  return result.summary.slice(0, 2);
}