import type {
  ReportChartSeriesPoint,
  ReportColumnType,
  ReportFilterOption,
  ReportFilterValue,
  ReportSort,
  ReportTableColumn,
  ReportTableRow,
} from "@/lib/reports/contracts";

const ENUM_LABELS: Record<string, string> = {
  approved: "Aprovado",
  active: "Ativo",
  canceled: "Cancelado",
  cancelled: "Cancelado",
  cash: "Dinheiro",
  commercial: "Comercial",
  compare: "Comparativo",
  completed: "Concluído",
  credit_card: "Cartão de crédito",
  debit_card: "Cartão de débito",
  delivered: "Entregue",
  draft: "Rascunho",
  finished: "Finalizado",
  in: "Entrada",
  in_production: "Em produção",
  inactive: "Inativo",
  manual: "Lançamento manual",
  out: "Saída",
  overdue: "Em atraso",
  paid: "Pago",
  partially_paid: "Parcialmente pago",
  pagar: "Contas a pagar",
  payment_link_receipt: "Recebimento de link",
  pdv: "PDV",
  pedido: "Pedido",
  pending: "Pendente",
  pending_production: "Pendente de produção",
  pix: "Pix",
  received: "Recebido",
  receber: "Contas a receber",
  rejected: "Rejeitado",
  sent: "Enviado",
  transfer: "Transferência",
};

export function toNumber(value: unknown) {
  const num = Number(value);
  return Number.isFinite(num) ? num : 0;
}

export function normalizeDate(value: unknown) {
  if (typeof value !== "string") return null;
  const trimmed = value.trim();
  if (!trimmed) return null;
  return trimmed.split("T")[0] || null;
}

export function startOfMonth(date = new Date()) {
  const next = new Date(date.getFullYear(), date.getMonth(), 1);
  return next.toISOString().slice(0, 10);
}

export function endOfToday(date = new Date()) {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString().slice(0, 10);
}

export function periodKey(date: string | null) {
  if (!date) return "";
  return date.slice(0, 7);
}

export function firstOrNull<T>(value: T | T[] | null | undefined): T | null {
  if (!value) return null;
  return Array.isArray(value) ? (value[0] ?? null) : value;
}

export function asArray(value: ReportFilterValue): string[] {
  if (Array.isArray(value)) return value.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
  if (typeof value === "string" && value.trim()) return [value.trim()];
  return [];
}

export function asString(value: ReportFilterValue) {
  if (typeof value === "string") return value.trim();
  return "";
}

export function matchesFilter(candidate: string | null | undefined, value: ReportFilterValue) {
  const list = asArray(value).map((item) => item.toLowerCase());
  if (list.length === 0) return true;
  const normalized = String(candidate || "").trim().toLowerCase();
  return list.includes(normalized);
}

export function matchesTextSearch(row: ReportTableRow, search: string) {
  const term = search.trim().toLowerCase();
  if (!term) return true;
  return Object.values(row).some((value) => String(value ?? "").toLowerCase().includes(term));
}

export function sortRows(rows: ReportTableRow[], columns: ReportTableColumn[], sort: ReportSort) {
  if (!sort) return rows;
  const column = columns.find((item) => item.id === sort.columnId);
  if (!column) return rows;
  const directionFactor = sort.direction === "asc" ? 1 : -1;

  return [...rows].sort((left, right) => {
    const a = left[sort.columnId];
    const b = right[sort.columnId];
    if (column.type === "number" || column.type === "currency" || column.type === "percent") {
      return (toNumber(a) - toNumber(b)) * directionFactor;
    }
    return String(a ?? "").localeCompare(String(b ?? ""), "pt-BR", { sensitivity: "base" }) * directionFactor;
  });
}

export function paginateRows(rows: ReportTableRow[], page: number, pageSize: number) {
  const safePage = Math.max(1, page);
  const safePageSize = Math.max(1, pageSize);
  const start = (safePage - 1) * safePageSize;
  return rows.slice(start, start + safePageSize);
}

export function uniqueOptions(items: Array<string | null | undefined>) {
  return Array.from(
    new Set(items.map((item) => String(item || "").trim()).filter(Boolean))
  )
    .sort((a, b) => a.localeCompare(b, "pt-BR", { sensitivity: "base" }))
    .map<ReportFilterOption>((item) => ({ value: item, label: item }));
}

export function normalizeReportSeriesLabel(value: string | null | undefined) {
  const normalized = String(value || "").trim().replace(/\s+/g, " ");
  if (!normalized || normalized === "—" || normalized === "-") return "Sem informação";

  const key = normalized.toLowerCase();
  if (key === "sem forma") return "Sem forma de pagamento";
  if (key === "sem forma de pagamento") return "Sem forma de pagamento";
  if (key === "sem categoria") return "Sem categoria";
  if (key === "sem item") return "Sem item";
  if (key === "sem valor") return "Sem valor";
  if (key === "sem movimentação" || key === "sem movimentacao") return "Sem movimentação";
  if (key.startsWith("sem ")) {
    return normalized.charAt(0).toUpperCase() + normalized.slice(1).toLowerCase();
  }

  return normalized;
}

export function consolidateSeries(points: ReportChartSeriesPoint[]) {
  const grouped = new Map<string, ReportChartSeriesPoint>();
  for (const point of points) {
    const label = normalizeReportSeriesLabel(point.label);
    const current = grouped.get(label);
    if (current) {
      current.value += point.value;
      if (typeof point.secondaryValue === "number") {
        current.secondaryValue = (current.secondaryValue || 0) + point.secondaryValue;
      }
      continue;
    }
    grouped.set(label, {
      label,
      value: point.value,
      secondaryValue: point.secondaryValue,
    });
  }
  return Array.from(grouped.values());
}

export function buildSeriesMap(points: Array<{ key: string; value: number }>) {
  const grouped = new Map<string, number>();
  for (const point of points) {
    const normalizedKey = normalizeReportSeriesLabel(point.key);
    grouped.set(normalizedKey, (grouped.get(normalizedKey) || 0) + point.value);
  }
  return Array.from(grouped.entries())
    .map<ReportChartSeriesPoint>(([label, value]) => ({ label, value }))
    .sort((a, b) => a.label.localeCompare(b.label, "pt-BR", { numeric: true }));
}

export function takeTopSeries(points: ReportChartSeriesPoint[], limit = 8) {
  return consolidateSeries(points)
    .sort((a, b) => b.value - a.value)
    .slice(0, limit);
}

export function formatValueByType(value: string | number | null, type: ReportColumnType) {
  if (value === null || value === "") return "—";
  if (type === "currency") {
    return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(toNumber(value));
  }
  if (type === "percent") {
    return `${toNumber(value).toFixed(2)}%`;
  }
  if (type === "number") {
    return new Intl.NumberFormat("pt-BR").format(toNumber(value));
  }
  if (type === "date") {
    const normalized = normalizeDate(value);
    if (!normalized) return "—";
    const [year, month, day] = normalized.split("-");
    return `${day}/${month}/${year}`;
  }
  return String(value);
}

export function humanizeEnumLabel(value: string | null | undefined) {
  const normalized = String(value || "").trim();
  if (!normalized) return "—";
  const key = normalized.toLowerCase();
  if (ENUM_LABELS[key]) return ENUM_LABELS[key];
  return normalized
    .replace(/_/g, " ")
    .split(" ")
    .filter(Boolean)
    .map((item) => item.charAt(0).toUpperCase() + item.slice(1).toLowerCase())
    .join(" ");
}

export function monthLabel(value: string) {
  if (!/^\d{4}-\d{2}$/.test(value)) return value;
  const [year, month] = value.split("-");
  const date = new Date(Number(year), Number(month) - 1, 1);
  return new Intl.DateTimeFormat("pt-BR", { month: "short", year: "2-digit" }).format(date);
}

export function safeJson<T>(value: unknown, fallback: T): T {
  if (!value) return fallback;
  if (typeof value === "object") return value as T;
  if (typeof value !== "string") return fallback;
  try {
    return JSON.parse(value) as T;
  } catch {
    return fallback;
  }
}
