"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  CalendarRange,
  ChevronDown,
  CircleDollarSign,
  CreditCard,
  FolderOpen,
  Hourglass,
  Landmark,
  Plus,
  TriangleAlert,
  UserRound,
} from "lucide-react";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { CustomSelect } from "@/components/ui/custom-select";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { ReportCharts } from "@/components/reports/report-charts";
import { ReportDataTable } from "@/components/reports/report-data-table";
import { getCompanyContext, shouldUseEmployeeUi } from "@/lib/company-context";
import type {
  ReportCustomConfig,
  ReportCustomUniverse,
  ReportExportFormat,
  ReportFilterOption,
  ReportChart,
  ReportKpi,
  ReportOptionsPayload,
  ReportPreset,
  ReportRequest,
  ReportResponse,
  ReportSort,
  ReportTabKey,
} from "@/lib/reports/contracts";
import {
  buildAppliedFiltersSummary,
  buildReportAuditMeta,
  type ReportAuditMeta,
} from "@/lib/reports/audit";
import { getSupabaseClient } from "@/lib/supabase";
import { formatValueByType, startOfMonth } from "@/lib/reports/utils";

type TabDefinition = {
  key: ReportTabKey;
  label: string;
};

type FilterFieldConfig = {
  key: string;
  label: string;
  options: ReportFilterOption[];
};

type KpiPresentation = {
  icon: typeof CircleDollarSign;
  accentClass: string;
  badgeClass: string;
};

const tabs: TabDefinition[] = [
  { key: "financial", label: "Financeiro" },
  { key: "sales", label: "Vendas" },
  { key: "products", label: "Produtos" },
  { key: "customers", label: "Clientes" },
  { key: "custom", label: "Personalizados" },
];

const defaultCustomConfig: ReportCustomConfig = {
  name: "",
  universe: "sales",
  columns: ["date", "client", "seller", "status", "paymentMethod", "value"],
  metrics: ["orders", "ticket", "conversion", "soldValue"],
  filters: {},
  groupBy: null,
  sort: null,
};

const customUniverseOptions: ReportFilterOption[] = [
  { value: "sales", label: "Vendas" },
  { value: "products", label: "Produtos" },
  { value: "customers", label: "Clientes" },
  { value: "financialCash", label: "Financeiro Caixa" },
  { value: "financialReceivables", label: "Financeiro Carteira" },
];

const defaultColumnsByUniverse: Record<ReportCustomUniverse, string[]> = {
  sales: ["date", "client", "seller", "status", "paymentMethod", "value"],
  products: ["product", "category", "quantity", "value", "profit", "saleStatus"],
  customers: ["client", "city", "state", "newClient", "activityStatus", "recurrenceStatus", "orderCount", "value"],
  financialCash: ["date", "recordType", "client", "paymentMethod", "bankAccount", "financialCategory", "value"],
  financialReceivables: ["date", "client", "financialCategory", "status", "type", "value"],
};

const defaultMetricsByUniverse: Record<ReportCustomUniverse, string[]> = {
  sales: ["orders", "ticket", "conversion", "soldValue"],
  products: ["soldQty", "revenue", "profit", "margin"],
  customers: ["newCustomers", "activeCustomers", "inactiveCustomers", "recurringCustomers", "avgTicket"],
  financialCash: ["received", "cashFlowNet"],
  financialReceivables: ["accountsReceivable", "overdue"],
};

function buildDefaultFilters() {
  return {
    startDate: startOfMonth(),
    endDate: new Date().toISOString().slice(0, 10),
    client: "",
    paymentMethod: "",
    status: "",
    bankAccount: "",
    financialCategory: "",
    channel: "",
    seller: "",
    category: "",
    supplier: "",
    city: "",
    state: "",
  };
}

function parseDateValue(value: string) {
  const date = new Date(`${value}T00:00:00`);
  return Number.isNaN(date.getTime()) ? null : date;
}

function toDateInputValue(date: Date) {
  return date.toISOString().slice(0, 10);
}

function toMonthKey(date: Date) {
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
}

function buildMonthKeysFromFilters(filters: Record<string, unknown>) {
  const startDate = parseDateValue(String(filters.startDate || ""));
  const endDate = parseDateValue(String(filters.endDate || ""));
  if (!startDate || !endDate || endDate < startDate) {
    return [];
  }

  const cursor = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
  const lastMonth = new Date(endDate.getFullYear(), endDate.getMonth(), 1);
  const keys: string[] = [];

  while (cursor <= lastMonth) {
    keys.push(toMonthKey(cursor));
    cursor.setMonth(cursor.getMonth() + 1);
  }

  return keys;
}

function formatMonthKey(key: string) {
  const [year, month] = key.split("-");
  const date = new Date(Number(year), Number(month) - 1, 1);
  return new Intl.DateTimeFormat("pt-BR", { month: "short", year: "2-digit" }).format(date);
}

function normalizeTemporalLabel(value: string) {
  return value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/\./g, "")
    .replace(/\s+/g, " ")
    .trim()
    .toLowerCase();
}

function buildTemporalSlotMap(series: ReportChart["series"], filters: Record<string, unknown>) {
  const monthKeys = buildMonthKeysFromFilters(filters);
  const candidates = monthKeys.map((key, index) => ({
    periodKey: key,
    slotKey: String(index),
    label: normalizeTemporalLabel(formatMonthKey(key)),
  }));
  const usedIndexes = new Set<number>();
  let nextCandidateIndex = 0;

  return series.map((point, index) => {
    const normalizedLabel = normalizeTemporalLabel(point.label);
    let candidateIndex = candidates.findIndex(
      (candidate, searchIndex) =>
        searchIndex >= nextCandidateIndex && !usedIndexes.has(searchIndex) && candidate.label === normalizedLabel
    );

    if (candidateIndex === -1) {
      candidateIndex = candidates.findIndex(
        (candidate, searchIndex) => !usedIndexes.has(searchIndex) && candidate.label === normalizedLabel
      );
    }

    if (candidateIndex === -1) {
      candidateIndex = candidates.findIndex((_, searchIndex) => !usedIndexes.has(searchIndex));
    }

    if (candidateIndex !== -1) {
      usedIndexes.add(candidateIndex);
      nextCandidateIndex = candidateIndex + 1;
      return {
        point,
        periodKey: candidates[candidateIndex].periodKey,
        slotKey: candidates[candidateIndex].slotKey,
      };
    }

    return {
      point,
      periodKey: `unmapped-${index}`,
      slotKey: `unmapped-${index}`,
    };
  });
}

function isFinancialTemporalChart(chartId: string) {
  return chartId === "receipts-period" || chartId === "cash-flow-period";
}

function buildPreviousPeriodFilters(filters: Record<string, string>) {
  const startDate = parseDateValue(filters.startDate);
  const endDate = parseDateValue(filters.endDate);
  if (!startDate || !endDate || endDate < startDate) {
    return filters;
  }

  const dayMs = 24 * 60 * 60 * 1000;
  const durationDays = Math.round((endDate.getTime() - startDate.getTime()) / dayMs) + 1;
  const previousEnd = new Date(startDate.getTime() - dayMs);
  const previousStart = new Date(previousEnd.getTime() - (durationDays - 1) * dayMs);

  return {
    ...filters,
    startDate: toDateInputValue(previousStart),
    endDate: toDateInputValue(previousEnd),
  };
}

function alignComparisonChartsByTemporalKey(
  currentReport: ReportResponse | null,
  comparisonReport: ReportResponse | null
) {
  if (!currentReport || !comparisonReport) return comparisonReport?.charts ?? null;
  if (currentReport.tab !== "financial") return comparisonReport.charts;

  const comparisonMap = new Map(comparisonReport.charts.map((chart) => [chart.id, chart]));

  return currentReport.charts.flatMap((chart) => {
    const comparisonChart = comparisonMap.get(chart.id);
    if (!comparisonChart) return [];

    if (!isFinancialTemporalChart(chart.id)) {
      return [comparisonChart];
    }

    const currentSlots = buildTemporalSlotMap(chart.series, currentReport.appliedFilters);
    const previousSlots = buildTemporalSlotMap(comparisonChart.series, comparisonReport.appliedFilters);
    const previousValuesBySlot = new Map(previousSlots.map((item) => [item.slotKey, item.point.value]));

    return [
      {
        ...comparisonChart,
        series: currentSlots.map((item) => ({
          label: item.point.label,
          value: previousValuesBySlot.get(item.slotKey) ?? 0,
        })),
      },
    ];
  });
}

function calculateVariation(current: number, previous: number | null | undefined) {
  if (previous === null || previous === undefined) return null;
  if (previous === 0) return current === 0 ? 0 : 100;
  return ((current - previous) / Math.abs(previous)) * 100;
}

function formatVariation(value: number | null) {
  if (value === null) return "Sem base";
  const signal = value > 0 ? "+" : value < 0 ? "" : "";
  return `${signal}${value.toFixed(1).replace(".", ",")}%`;
}

function getVariationClass(value: number | null) {
  if (value === null) return "bg-slate-100 text-slate-500";
  if (value < 0) return "bg-red-50 text-red-600";
  if (value > 0) return "bg-emerald-50 text-emerald-600";
  return "bg-slate-100 text-slate-500";
}

function getKpiPresentation(kpi: ReportKpi): KpiPresentation {
  const key = `${kpi.id} ${kpi.label}`.toLowerCase();
  if (key.includes("cashflownet") || key.includes("fluxo de caixa")) {
    return {
      icon: Landmark,
      accentClass: "bg-violet-500/10 text-violet-600",
      badgeClass: "bg-violet-50 text-violet-600",
    };
  }
  if (key.includes("accountsreceivable") || key.includes("contas a receber") || key.includes("carteira")) {
    return {
      icon: Hourglass,
      accentClass: "bg-amber-500/10 text-amber-600",
      badgeClass: "bg-amber-50 text-amber-600",
    };
  }
  if (key.includes("received") || key.includes("valor recebido") || key.includes("recebido")) {
    return {
      icon: CreditCard,
      accentClass: "bg-emerald-500/10 text-emerald-600",
      badgeClass: "bg-emerald-50 text-emerald-600",
    };
  }
  if (key.includes("pending") || key.includes("pend")) {
    return {
      icon: Hourglass,
      accentClass: "bg-amber-500/10 text-amber-600",
      badgeClass: "bg-amber-50 text-amber-600",
    };
  }
  if (key.includes("overdue") || key.includes("atras")) {
    return {
      icon: TriangleAlert,
      accentClass: "bg-red-500/10 text-red-600",
      badgeClass: "bg-red-50 text-red-600",
    };
  }
  if (key.includes("client")) {
    return {
      icon: UserRound,
      accentClass: "bg-sky-500/10 text-sky-600",
      badgeClass: "bg-sky-50 text-sky-600",
    };
  }
  return {
    icon: CircleDollarSign,
    accentClass: "bg-violet-500/10 text-violet-600",
    badgeClass: "bg-violet-50 text-violet-600",
  };
}

function makeOptions(items: ReportFilterOption[], includeAll = true) {
  return [
    ...(includeAll ? [{ value: "", label: "Todos" }] : []),
    ...items,
  ];
}

function arraysMatch(left: string[], right: string[]) {
  if (left.length !== right.length) return false;
  return left.every((value, index) => value === right[index]);
}

const PDF_MARGIN_X = 34;
const PDF_MARGIN_TOP = 28;
const PDF_HEADER_HEIGHT = 54;
const PDF_FOOTER_HEIGHT = 20;
const PDF_BRAND = [76, 70, 182] as const;
const PDF_ACCENT = [10, 192, 214] as const;
const PDF_TEXT = [15, 23, 42] as const;
const PDF_MUTED = [100, 116, 139] as const;
const PDF_BORDER = [226, 232, 240] as const;
function drawPdfHeader(pdf: jsPDF, report: ReportResponse, audit: ReportAuditMeta, pageTitle?: string) {
  const pageWidth = pdf.internal.pageSize.getWidth();
  pdf.setDrawColor(...PDF_BORDER);
  pdf.line(PDF_MARGIN_X, PDF_MARGIN_TOP + PDF_HEADER_HEIGHT, pageWidth - PDF_MARGIN_X, PDF_MARGIN_TOP + PDF_HEADER_HEIGHT);

  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(10);
  pdf.setTextColor(...PDF_BRAND);
  pdf.text("Ktalogu ERP", PDF_MARGIN_X, PDF_MARGIN_TOP + 10);

  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(18);
  pdf.setTextColor(...PDF_TEXT);
  pdf.text(pageTitle || report.title, PDF_MARGIN_X, PDF_MARGIN_TOP + 30);

  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(9);
  pdf.setTextColor(...PDF_MUTED);
  pdf.text(`Empresa: ${report.company.companyName}`, PDF_MARGIN_X, PDF_MARGIN_TOP + 44);
  pdf.text(`Emissao: ${audit.generatedAt}`, pageWidth - PDF_MARGIN_X, PDF_MARGIN_TOP + 16, { align: "right" });
  pdf.text(
    `Periodo: ${String(report.appliedFilters.startDate || "—")} ate ${String(report.appliedFilters.endDate || "—")}`,
    pageWidth - PDF_MARGIN_X,
    PDF_MARGIN_TOP + 30,
    { align: "right" }
  );
  pdf.text(`Relatorio: ${report.title}`, pageWidth - PDF_MARGIN_X, PDF_MARGIN_TOP + 44, { align: "right" });
}

function drawPdfFooter(pdf: jsPDF) {
  const pageWidth = pdf.internal.pageSize.getWidth();
  const pageHeight = pdf.internal.pageSize.getHeight();
  const pageNumber = pdf.getCurrentPageInfo().pageNumber;
  pdf.setDrawColor(...PDF_BORDER);
  pdf.line(PDF_MARGIN_X, pageHeight - PDF_FOOTER_HEIGHT - 8, pageWidth - PDF_MARGIN_X, pageHeight - PDF_FOOTER_HEIGHT - 8);
  pdf.setFontSize(8);
  pdf.setTextColor(...PDF_MUTED);
  pdf.text(`Pagina ${pageNumber}`, pageWidth - PDF_MARGIN_X, pageHeight - 8, { align: "right" });
}

function drawPdfKpis(pdf: jsPDF, kpis: ReportKpi[], startY: number) {
  const pageWidth = pdf.internal.pageSize.getWidth();
  const gap = 12;
  const cardWidth = (pageWidth - PDF_MARGIN_X * 2 - gap * 3) / 4;
  const cardHeight = 54;

  kpis.forEach((kpi, index) => {
    const x = PDF_MARGIN_X + index * (cardWidth + gap);
    pdf.setDrawColor(...PDF_BORDER);
    pdf.setFillColor(255, 255, 255);
    pdf.roundedRect(x, startY, cardWidth, cardHeight, 8, 8, "FD");
    pdf.setFont("helvetica", "normal");
    pdf.setFontSize(8);
    pdf.setTextColor(...PDF_MUTED);
    pdf.text(kpi.label, x + 10, startY + 15);
    pdf.setFont("helvetica", "bold");
    pdf.setFontSize(12);
    pdf.setTextColor(...PDF_TEXT);
    pdf.text(
      formatValueByType(kpi.value, kpi.type === "currency" ? "currency" : kpi.type === "percent" ? "percent" : "number"),
      x + 10,
      startY + 35
    );
  });

  return startY + cardHeight;
}

function drawPdfFilterSummary(pdf: jsPDF, report: ReportResponse, startY: number) {
  const pageWidth = pdf.internal.pageSize.getWidth();
  const lines = buildAppliedFiltersSummary(report);
  const boxHeight = lines.length > 0 ? 54 : 40;
  pdf.setDrawColor(...PDF_BORDER);
  pdf.setFillColor(248, 250, 252);
  pdf.roundedRect(PDF_MARGIN_X, startY, pageWidth - PDF_MARGIN_X * 2, boxHeight, 8, 8, "FD");
  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(9);
  pdf.setTextColor(...PDF_TEXT);
  pdf.text("Contexto do relatorio", PDF_MARGIN_X + 10, startY + 15);
  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(8);
  pdf.setTextColor(...PDF_MUTED);
  const summary = lines.length > 0 ? lines.join("  •  ") : "Sem filtros complementares aplicados.";
  const wrapped = pdf.splitTextToSize(summary, pageWidth - PDF_MARGIN_X * 2 - 20);
  pdf.text(wrapped, PDF_MARGIN_X + 10, startY + 30);
  return startY + boxHeight;
}

function drawPdfAuditSummary(pdf: jsPDF, audit: ReportAuditMeta, startY: number) {
  const pageWidth = pdf.internal.pageSize.getWidth();
  const lines = [
    `Usuario gerador: ${audit.generatedBy}`,
    `Timezone: ${audit.timezone}`,
    `Criterio de ordenacao: ${audit.sortLabel}`,
    `Total de registros: ${audit.totalRows}`,
    `Versao da regra: ${audit.ruleVersion}`,
  ];

  const wrappedLines = lines.flatMap((line) => pdf.splitTextToSize(line, pageWidth - PDF_MARGIN_X * 2 - 20));
  const boxHeight = Math.max(58, 18 + wrappedLines.length * 10);

  pdf.setDrawColor(...PDF_BORDER);
  pdf.setFillColor(255, 255, 255);
  pdf.roundedRect(PDF_MARGIN_X, startY, pageWidth - PDF_MARGIN_X * 2, boxHeight, 8, 8, "FD");
  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(9);
  pdf.setTextColor(...PDF_TEXT);
  pdf.text("Metadados de auditoria", PDF_MARGIN_X + 10, startY + 15);
  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(8);
  pdf.setTextColor(...PDF_MUTED);
  pdf.text(wrappedLines, PDF_MARGIN_X + 10, startY + 30);

  return startY + boxHeight;
}

function drawChartAxis(pdf: jsPDF, x: number, y: number, width: number, height: number) {
  pdf.setDrawColor(...PDF_BORDER);
  for (let step = 0; step <= 3; step += 1) {
    const lineY = y + (height / 3) * step;
    pdf.line(x, lineY, x + width, lineY);
  }
}

function getPdfLineRange(series: ReportChart["series"]) {
  const values = series.map((item) => Number(item.value) || 0);
  const min = Math.min(0, ...values);
  const max = Math.max(0, ...values);

  if (min === max) {
    return { min, max: max === 0 ? 1 : max };
  }

  return { min, max };
}

function projectPdfLineY(value: number, min: number, max: number, y: number, height: number) {
  const range = max - min || 1;
  return y + height - ((value - min) / range) * height;
}

function drawLineLikeChart(pdf: jsPDF, chart: ReportChart, x: number, y: number, width: number, height: number) {
  const series = chart.series.slice(0, 8);
  const plotHeight = height - 16;
  const { min, max } = getPdfLineRange(series);
  drawChartAxis(pdf, x, y, width, height);
  const baselineY = projectPdfLineY(0, min, max, y + 8, plotHeight);
  pdf.setDrawColor(...PDF_MUTED);
  pdf.setLineWidth(0.8);
  pdf.line(x, baselineY, x + width, baselineY);
  pdf.setDrawColor(...PDF_ACCENT);
  pdf.setLineWidth(1.6);
  let previousPoint: { x: number; y: number } | null = null;

  series.forEach((point, index) => {
    const pointX = x + (series.length === 1 ? width / 2 : (width / Math.max(1, series.length - 1)) * index);
    const pointY = projectPdfLineY(point.value, min, max, y + 8, plotHeight);
    if (previousPoint) {
      pdf.line(previousPoint.x, previousPoint.y, pointX, pointY);
    }
    pdf.setFillColor(...PDF_BRAND);
    pdf.circle(pointX, pointY, 2.5, "F");
    pdf.setFontSize(7);
    pdf.setTextColor(...PDF_MUTED);
    pdf.text(point.label, pointX, y + height + 12, { align: "center", maxWidth: 60 });
    previousPoint = { x: pointX, y: pointY };
  });
}

function drawColumnChart(pdf: jsPDF, chart: ReportChart, x: number, y: number, width: number, height: number) {
  const series = chart.series.slice(0, 8);
  const max = Math.max(1, ...series.map((item) => item.value));
  drawChartAxis(pdf, x, y, width, height);
  const barGap = 10;
  const barWidth = Math.max(18, (width - barGap * Math.max(0, series.length - 1)) / Math.max(1, series.length));

  series.forEach((point, index) => {
    const barHeight = (point.value / max) * (height - 20);
    const barX = x + index * (barWidth + barGap);
    const barY = y + height - barHeight;
    pdf.setFillColor(...PDF_BRAND);
    pdf.roundedRect(barX, barY, barWidth, barHeight, 4, 4, "F");
    pdf.setFontSize(7);
    pdf.setTextColor(...PDF_MUTED);
    pdf.text(point.label, barX + barWidth / 2, y + height + 12, { align: "center", maxWidth: barWidth + 10 });
  });
}

function drawHorizontalBars(pdf: jsPDF, chart: ReportChart, x: number, y: number, width: number, height: number) {
  const series = chart.series.slice(0, 6);
  const max = Math.max(1, ...series.map((item) => item.value));
  const rowHeight = height / Math.max(1, series.length);

  series.forEach((point, index) => {
    const rowY = y + index * rowHeight;
    const barWidth = (point.value / max) * (width - 150);
    pdf.setFontSize(8);
    pdf.setTextColor(...PDF_TEXT);
    pdf.text(point.label, x, rowY + 12, { maxWidth: 120 });
    pdf.setFillColor(241, 245, 249);
    pdf.roundedRect(x + 126, rowY + 3, width - 150, 10, 4, 4, "F");
    pdf.setFillColor(...PDF_BRAND);
    pdf.roundedRect(x + 126, rowY + 3, Math.max(6, barWidth), 10, 4, 4, "F");
    pdf.setTextColor(...PDF_MUTED);
    pdf.text(
      formatValueByType(point.value, chart.format === "currency" ? "currency" : chart.format === "percent" ? "percent" : "number"),
      x + width,
      rowY + 12,
      { align: "right" }
    );
  });
}

function drawStackDistribution(pdf: jsPDF, chart: ReportChart, x: number, y: number, width: number) {
  const series = chart.series.slice(0, 6);
  const total = Math.max(1, series.reduce((sum, item) => sum + item.value, 0));
  const colors = [
    [76, 70, 182],
    [10, 192, 214],
    [16, 185, 129],
    [245, 158, 11],
    [239, 68, 68],
    [100, 116, 139],
  ] as const;

  let currentX = x;
  series.forEach((point, index) => {
    const segmentWidth = (point.value / total) * width;
    const [r, g, b] = colors[index % colors.length];
    pdf.setFillColor(r, g, b);
    pdf.roundedRect(currentX, y, Math.max(10, segmentWidth), 18, 4, 4, "F");
    currentX += segmentWidth;
  });

  series.forEach((point, index) => {
    const legendY = y + 36 + index * 18;
    const [r, g, b] = colors[index % colors.length];
    pdf.setFillColor(r, g, b);
    pdf.roundedRect(x, legendY - 8, 10, 10, 2, 2, "F");
    pdf.setFontSize(8);
    pdf.setTextColor(...PDF_TEXT);
    pdf.text(point.label, x + 16, legendY, { maxWidth: width - 120 });
    pdf.setTextColor(...PDF_MUTED);
    pdf.text(
      `${formatValueByType(point.value, chart.format === "currency" ? "currency" : chart.format === "percent" ? "percent" : "number")} • ${((point.value / total) * 100).toFixed(1)}%`,
      x + width,
      legendY,
      { align: "right" }
    );
  });
}

function drawPdfChartCard(pdf: jsPDF, chart: ReportChart, x: number, y: number, width: number, height: number) {
  pdf.setDrawColor(...PDF_BORDER);
  pdf.setFillColor(255, 255, 255);
  pdf.roundedRect(x, y, width, height, 10, 10, "FD");
  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(11);
  pdf.setTextColor(...PDF_TEXT);
  pdf.text(chart.title, x + 14, y + 18);
  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(8);
  pdf.setTextColor(...PDF_MUTED);
  pdf.text(`${chart.series.length} pontos analisados`, x + 14, y + 32);

  const plotX = x + 14;
  const plotY = y + 48;
  const plotWidth = width - 28;
  const plotHeight = height - 66;

  if (chart.series.length === 0) {
    pdf.text("Sem dados suficientes para este grafico.", plotX, plotY + 16);
    return;
  }

  if (chart.type === "column" || chart.type === "bar") {
    drawColumnChart(pdf, chart, plotX, plotY, plotWidth, plotHeight - 18);
    return;
  }

  if (chart.type === "ranking" || chart.type === "compare") {
    drawHorizontalBars(pdf, chart, plotX, plotY, plotWidth, plotHeight);
    return;
  }

  if (chart.type === "donut") {
    drawStackDistribution(pdf, chart, plotX, plotY, plotWidth);
    return;
  }

  drawLineLikeChart(pdf, chart, plotX, plotY, plotWidth, plotHeight - 18);
}

function renderPdfCharts(pdf: jsPDF, report: ReportResponse, audit: ReportAuditMeta) {
  let currentChartIndex = 0;
  while (currentChartIndex < report.charts.length) {
    if (currentChartIndex > 0) {
      pdf.addPage("a4", "landscape");
    }

    drawPdfHeader(pdf, report, audit, `${report.title} • Graficos`);
    let cursorY = PDF_MARGIN_TOP + PDF_HEADER_HEIGHT + 16;

    if (currentChartIndex === 0) {
      cursorY = drawPdfKpis(pdf, report.kpis, cursorY) + 14;
      cursorY = drawPdfFilterSummary(pdf, report, cursorY) + 16;
      cursorY = drawPdfAuditSummary(pdf, audit, cursorY) + 16;
    }

    const pageWidth = pdf.internal.pageSize.getWidth();
    const cardGap = 14;
    const remainingCharts = report.charts.slice(currentChartIndex, currentChartIndex + 2);

    if (remainingCharts.length === 1) {
      drawPdfChartCard(
        pdf,
        remainingCharts[0],
        PDF_MARGIN_X,
        cursorY,
        pageWidth - PDF_MARGIN_X * 2,
        300
      );
    } else {
      const cardWidth = (pageWidth - PDF_MARGIN_X * 2 - cardGap) / 2;
      drawPdfChartCard(pdf, remainingCharts[0], PDF_MARGIN_X, cursorY, cardWidth, 300);
      drawPdfChartCard(pdf, remainingCharts[1], PDF_MARGIN_X + cardWidth + cardGap, cursorY, cardWidth, 300);
    }

    drawPdfFooter(pdf);
    currentChartIndex += 2;
  }
}

async function fetchJson<T>(url: string, init?: RequestInit) {
  const response = await fetch(url, {
    ...init,
    headers: { "Content-Type": "application/json", ...(init?.headers || {}) },
    cache: "no-store",
  });
  const payload = (await response.json().catch(() => null)) as T | { error?: string } | null;
  if (!response.ok) {
    throw new Error((payload as { error?: string } | null)?.error || "Falha na operação.");
  }
  return payload as T;
}

async function downloadFromResponse(response: Response, fallbackName: string) {
  if (!response.ok) {
    const payload = (await response.json().catch(() => null)) as { error?: string } | null;
    throw new Error(payload?.error || "Falha ao baixar arquivo.");
  }
  const blob = await response.blob();
  const disposition = response.headers.get("Content-Disposition") || "";
  const match = disposition.match(/filename="([^"]+)"/i);
  const filename = match?.[1] || fallbackName;
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = filename;
  anchor.click();
  URL.revokeObjectURL(url);
}

export function ReportPageClient() {
  const [activeTab, setActiveTab] = useState<ReportTabKey>("financial");
  const [filters, setFilters] = useState<Record<string, string>>(buildDefaultFilters());
  const [report, setReport] = useState<ReportResponse | null>(null);
  const [previousReport, setPreviousReport] = useState<ReportResponse | null>(null);
  const [exportUserName, setExportUserName] = useState("Usuario nao identificado");
  const [options, setOptions] = useState<ReportOptionsPayload | null>(null);
  const [loading, setLoading] = useState(true);
  /* Quando a geracao falha, o usuario precisa ver o motivo. Antes a tela
     ficava em branco e parecia que o relatorio simplesmente nao existia. */
  const [falha, setFalha] = useState<string | null>(null);
  const [search, setSearch] = useState("");
  const [page, setPage] = useState(1);
  const [pageSize] = useState(25);
  const [sort, setSort] = useState<ReportSort>(null);
  const [visibleColumnIds, setVisibleColumnIds] = useState<string[]>([]);
  const [saveDialogOpen, setSaveDialogOpen] = useState(false);
  const [presetName, setPresetName] = useState("");
  const [presetScope, setPresetScope] = useState<"company" | "user">("company");
  const [customConfig, setCustomConfig] = useState<ReportCustomConfig>(defaultCustomConfig);
  const [busyExport, setBusyExport] = useState<ReportExportFormat | null>(null);
  const [busySave, setBusySave] = useState(false);
  const [presetMenuOpen, setPresetMenuOpen] = useState(false);
  const presetMenuRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    void loadMetadata();
  }, []);

  useEffect(() => {
    async function loadExportUserName() {
      const supabase = getSupabaseClient();
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) return;

      const ctx = await getCompanyContext(supabase, user);
      const isEmployee = shouldUseEmployeeUi(ctx, user.id);

      if (isEmployee) {
        const { data: employeeProfile } = await supabase
          .from("employee_profiles")
          .select("full_name")
          .eq("user_id", user.id)
          .maybeSingle();

        if (employeeProfile?.full_name) {
          setExportUserName(String(employeeProfile.full_name).trim());
          return;
        }
      }

      const profileName = user.user_metadata?.full_name || user.user_metadata?.name;
      if (profileName) {
        setExportUserName(String(profileName).trim());
        return;
      }

      const { data: profile } = await supabase
        .from("profiles")
        .select("store_name")
        .eq("id", user.id)
        .maybeSingle();

      if (profile?.store_name) {
        setExportUserName(String(profile.store_name).trim());
        return;
      }

      const emailName = user.email?.split("@")[0];
      if (emailName) {
        setExportUserName(emailName);
      }
    }

    void loadExportUserName();
  }, []);

  useEffect(() => {
    function handleClickOutside(event: MouseEvent) {
      if (presetMenuRef.current && !presetMenuRef.current.contains(event.target as Node)) {
        setPresetMenuOpen(false);
      }
    }

    window.addEventListener("mousedown", handleClickOutside);
    return () => window.removeEventListener("mousedown", handleClickOutside);
  }, []);

  async function loadMetadata() {
    setLoading(true);
    try {
      const payload = await fetchJson<ReportOptionsPayload>("/api/reports");
      setOptions(payload);
    } finally {
      setLoading(false);
    }
  }

  const loadReport = useCallback(async (overrides?: Partial<ReportRequest>) => {
    setLoading(true);
    setFalha(null);
    try {
      const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
      const currentRequest = {
        tab: overrides?.tab || activeTab,
        filters,
        page: overrides?.page ?? page,
        pageSize,
        search,
        sort,
        visibleColumnIds,
        custom: activeTab === "custom" ? { ...customConfig, filters } : null,
        exportAllRows: overrides?.exportAllRows ?? false,
        skipAudit: overrides?.skipAudit ?? false,
      } satisfies ReportRequest;

      const comparisonRequest = {
        ...currentRequest,
        filters: buildPreviousPeriodFilters(filters),
        page: 1,
        search: "",
        exportAllRows: false,
      } satisfies ReportRequest;

      const [payload, comparisonPayload] = await Promise.all([
        fetchJson<ReportResponse>("/api/reports", {
          method: "POST",
          body: JSON.stringify({
            ...currentRequest,
            auditContext: {
              generatedBy: exportUserName,
              timezone,
            },
          }),
        }),
        fetchJson<ReportResponse>("/api/reports", {
          method: "POST",
          body: JSON.stringify({
            ...comparisonRequest,
            skipAudit: true,
            auditContext: {
              generatedBy: exportUserName,
              timezone,
            },
          }),
        }).catch(() => null),
      ]);
      setReport(payload);
      setPreviousReport(comparisonPayload);
      setVisibleColumnIds((current) => {
        if (current.length > 0 && payload.columns.some((column) => current.includes(column.id))) {
          const next = current.filter((id) => payload.columns.some((column) => column.id === id));
          return arraysMatch(current, next) ? current : next;
        }
        const defaults = payload.columns.filter((column) => column.visibleByDefault !== false).map((column) => column.id);
        return arraysMatch(current, defaults) ? current : defaults;
      });
    } catch (erro) {
      setReport(null);
      setPreviousReport(null);
      setFalha(erro instanceof Error ? erro.message : "Nao foi possivel gerar o relatorio.");
    } finally {
      setLoading(false);
    }
  }, [activeTab, customConfig, exportUserName, filters, page, pageSize, search, sort, visibleColumnIds]);

  useEffect(() => {
    if (!options) return;
    void loadReport();
  }, [options, loadReport]);

  const customUniverseFields = useMemo(
    () => options?.customFields.filter((field) => field.universes?.includes(customConfig.universe)) ?? [],
    [customConfig.universe, options]
  );

  const customUniverseMetrics = useMemo(
    () => options?.customMetrics.filter((metric) => metric.universes.includes(customConfig.universe)) ?? [],
    [customConfig.universe, options]
  );

  useEffect(() => {
    if (!options) return;
    const allowedFieldIds = new Set(customUniverseFields.map((field) => field.id));
    const allowedMetricIds = new Set(customUniverseMetrics.map((metric) => metric.id));
    const fallbackColumns = defaultColumnsByUniverse[customConfig.universe];
    const fallbackMetrics = defaultMetricsByUniverse[customConfig.universe];

    setCustomConfig((current) => {
      const columns = current.columns.filter((columnId) => allowedFieldIds.has(columnId));
      const metrics = current.metrics.filter((metricId) => allowedMetricIds.has(metricId));
      const groupBy = current.groupBy && allowedFieldIds.has(current.groupBy) ? current.groupBy : null;
      const sort =
        current.sort && allowedFieldIds.has(current.sort.columnId)
          ? current.sort
          : null;
      const nextColumns = columns.length > 0 ? columns : fallbackColumns.filter((columnId) => allowedFieldIds.has(columnId));
      const nextMetrics = metrics.length > 0 ? metrics : fallbackMetrics.filter((metricId) => allowedMetricIds.has(metricId));
      if (
        arraysMatch(current.columns, nextColumns) &&
        arraysMatch(current.metrics, nextMetrics) &&
        current.groupBy === groupBy &&
        JSON.stringify(current.sort) === JSON.stringify(sort)
      ) {
        return current;
      }
      return {
        ...current,
        columns: nextColumns,
        metrics: nextMetrics,
        groupBy,
        sort,
      };
    });
  }, [customConfig.universe, customUniverseFields, customUniverseMetrics, options]);

  async function refreshCurrentReport() {
    setPage(1);
    await loadReport({ page: 1 });
  }

  function updateFilter(key: string, value: string) {
    setFilters((current) => ({ ...current, [key]: value }));
    setPage(1);
  }

  function resetFilters() {
    setFilters(buildDefaultFilters());
    setSearch("");
    setSort(null);
    setPage(1);
  }

  const tabFilterFields = useMemo<FilterFieldConfig[]>(() => {
    if (!options) return [];
    if (activeTab === "financial") {
      return [
        { key: "client", label: "Cliente", options: makeOptions(options.clients) },
        { key: "paymentMethod", label: "Forma de pagamento", options: makeOptions(options.financialPaymentMethods) },
        { key: "status", label: "Status financeiro", options: makeOptions(options.financialStatuses) },
        { key: "bankAccount", label: "Conta bancária", options: makeOptions(options.bankAccounts) },
        { key: "financialCategory", label: "Categoria financeira", options: makeOptions(options.financialCategories) },
        { key: "channel", label: "Canal financeiro", options: makeOptions(options.channels) },
      ];
    }
    if (activeTab === "sales") {
      return [
        { key: "seller", label: "Vendedor", options: makeOptions(options.sellers) },
        { key: "client", label: "Cliente", options: makeOptions(options.clients) },
        { key: "status", label: "Status", options: makeOptions(options.statuses) },
      ];
    }
    if (activeTab === "products") {
      return [
        { key: "category", label: "Categoria", options: makeOptions(options.categories) },
        { key: "supplier", label: "Fornecedor", options: makeOptions(options.suppliers) },
      ];
    }
    if (activeTab === "customers") {
      return [
        { key: "client", label: "Cliente", options: makeOptions(options.clients) },
        { key: "seller", label: "Vendedor", options: makeOptions(options.sellers) },
        { key: "channel", label: "Canal", options: makeOptions(options.channels) },
        { key: "city", label: "Cidade", options: makeOptions(options.cities) },
        { key: "state", label: "Estado", options: makeOptions(options.states) },
      ];
    }
    const universeSpecificFilters =
      customConfig.universe === "sales"
        ? [
            { key: "seller", label: "Vendedor", options: makeOptions(options.sellers) },
            { key: "client", label: "Cliente", options: makeOptions(options.clients) },
            { key: "status", label: "Status", options: makeOptions(options.statuses) },
          ]
        : customConfig.universe === "products"
          ? [
              { key: "category", label: "Categoria", options: makeOptions(options.categories) },
              { key: "supplier", label: "Fornecedor", options: makeOptions(options.suppliers) },
            ]
          : customConfig.universe === "financialCash" || customConfig.universe === "financialReceivables"
            ? [
                { key: "client", label: "Cliente", options: makeOptions(options.clients) },
                { key: "paymentMethod", label: "Forma de pagamento", options: makeOptions(options.financialPaymentMethods) },
                { key: "status", label: "Status financeiro", options: makeOptions(options.financialStatuses) },
                { key: "bankAccount", label: "Conta bancária", options: makeOptions(options.bankAccounts) },
                { key: "financialCategory", label: "Categoria financeira", options: makeOptions(options.financialCategories) },
                { key: "channel", label: "Canal financeiro", options: makeOptions(options.channels) },
              ]
          : [
              { key: "client", label: "Cliente", options: makeOptions(options.clients) },
              { key: "seller", label: "Vendedor", options: makeOptions(options.sellers) },
              { key: "channel", label: "Canal", options: makeOptions(options.channels) },
              { key: "city", label: "Cidade", options: makeOptions(options.cities) },
              { key: "state", label: "Estado", options: makeOptions(options.states) },
            ];

    return [
      { key: "universe", label: "Universo", options: makeOptions(customUniverseOptions, false) },
      {
        key: "groupBy",
        label: "Agrupar por",
        options: makeOptions(customUniverseFields.map((field) => ({ value: field.id, label: field.label }))),
      },
      {
        key: "sortBy",
        label: "Ordenação",
        options: makeOptions(customUniverseFields.map((field) => ({ value: field.id, label: field.label }))),
      },
      ...universeSpecificFilters,
    ];
  }, [activeTab, customConfig.universe, customUniverseFields, options]);

  const activePresets = useMemo(
    () => options?.presets.filter((preset) => preset.reportKey === activeTab) ?? [],
    [activeTab, options]
  );

  const kpiCards = useMemo(() => {
    if (!report) return [];
    return report.kpis.map((kpi) => {
      const previousValue = previousReport?.kpis.find((item) => item.id === kpi.id)?.value ?? null;
      return {
        ...kpi,
        previousValue,
        variation: calculateVariation(kpi.value, previousValue),
        presentation: getKpiPresentation(kpi),
      };
    });
  }, [previousReport, report]);

  const alignedComparisonCharts = useMemo(
    () => alignComparisonChartsByTemporalKey(report, previousReport),
    [previousReport, report]
  );

  async function handleTabChange(tab: ReportTabKey) {
    setActiveTab(tab);
    setPage(1);
    setSort(null);
    setSearch("");
  }

  async function loadFullReportForExport() {
    const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
    const exportRequest = {
      tab: activeTab,
      filters,
      page: 1,
      pageSize: 1000,
      search,
      sort,
      visibleColumnIds,
      custom: activeTab === "custom" ? { ...customConfig, filters } : null,
      exportAllRows: true,
      skipAudit: true,
    } satisfies ReportRequest;
    const payload = await fetchJson<ReportResponse>("/api/reports", {
      method: "POST",
      body: JSON.stringify({
        ...exportRequest,
        auditContext: {
          generatedBy: exportUserName,
          timezone,
        },
      }),
    });
    return payload;
  }

  function buildPrintableHtml(fullReport: ReportResponse, audit: ReportAuditMeta) {
    const filterSummary = audit.filterSummary.length > 0 ? audit.filterSummary.join(" • ") : "Sem filtros complementares aplicados.";
    const header = `
      <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;border-bottom:1px solid #e2e8f0;padding-bottom:12px;">
        <div>
          <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.12em;color:#64748b;">Ktalogu ERP</div>
          <h1 style="margin:6px 0 0;font-size:24px;color:#0f172a;">${fullReport.title}</h1>
          <div style="font-size:12px;color:#64748b;margin-top:6px;">Emitido em ${audit.generatedAt}</div>
          <div style="font-size:12px;color:#64748b;">Período: ${String(fullReport.appliedFilters.startDate || "—")} até ${String(fullReport.appliedFilters.endDate || "—")}</div>
        </div>
        <div style="text-align:right;">
          <div style="font-size:18px;font-weight:700;color:#1e293b;">${fullReport.company.companyName}</div>
          <div style="font-size:12px;color:#64748b;">${fullReport.company.companyDocument || ""}</div>
          <div style="font-size:12px;color:#64748b;">Empresa ID: ${fullReport.company.companyId}</div>
        </div>
      </div>
    `;
    const auditBox = `
      <div style="margin-bottom:18px;border:1px solid #e2e8f0;border-radius:12px;padding:12px;background:#f8fafc;">
        <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:#334155;margin-bottom:8px;">Metadados de auditoria</div>
        <div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px 16px;font-size:12px;color:#475569;">
          <div><strong>Usuário gerador:</strong> ${audit.generatedBy}</div>
          <div><strong>Timezone:</strong> ${audit.timezone}</div>
          <div><strong>Critério de ordenação:</strong> ${audit.sortLabel}</div>
          <div><strong>Total de registros:</strong> ${audit.totalRows}</div>
          <div style="grid-column:1 / -1;"><strong>Versão da regra:</strong> ${audit.ruleVersion}</div>
          <div style="grid-column:1 / -1;"><strong>Filtros aplicados:</strong> ${filterSummary}</div>
        </div>
      </div>
    `;
    const indicators = fullReport.kpis
      .map(
        (kpi) => `<div style="border:1px solid #e2e8f0;border-radius:10px;padding:10px 12px;">
          <div style="font-size:11px;text-transform:uppercase;color:#64748b;">${kpi.label}</div>
          <div style="margin-top:4px;font-size:18px;font-weight:700;color:#0f172a;">${formatValueByType(kpi.value, kpi.type === "currency" ? "currency" : kpi.type === "percent" ? "percent" : "number")}</div>
        </div>`
      )
      .join("");
    const tableHeader = fullReport.columns
      .filter((column) => visibleColumnIds.includes(column.id))
      .map((column) => `<th style="padding:10px;border-bottom:1px solid #e2e8f0;text-align:left;font-size:12px;color:#475569;">${column.label}</th>`)
      .join("");
    const tableBody = fullReport.rows
      .map(
        (row) =>
          `<tr>${fullReport.columns
            .filter((column) => visibleColumnIds.includes(column.id))
            .map((column) => `<td style="padding:10px;border-bottom:1px solid #f1f5f9;font-size:12px;color:#0f172a;">${formatValueByType(row[column.id] ?? null, column.type)}</td>`)
            .join("")}</tr>`
      )
      .join("");
    return `
      <html>
        <head>
          <title>${fullReport.title}</title>
          <style>
            @page { size: A4 landscape; margin: 16mm; }
            thead { display: table-header-group; }
            tr, td, th { page-break-inside: avoid; }
          </style>
        </head>
        <body style="font-family:Arial,sans-serif;padding:24px;color:#0f172a;">
          ${header}
          ${auditBox}
          <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:18px;">${indicators}</div>
          <table style="width:100%;border-collapse:collapse;"> 
            <thead><tr>${tableHeader}</tr></thead>
            <tbody>${tableBody}</tbody>
          </table>
        </body>
      </html>
    `;
  }

  async function exportReport(format: ReportExportFormat) {
    if (!report) return;
    setBusyExport(format);
    try {
      const fullReport = await loadFullReportForExport();
      const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
      const audit = buildReportAuditMeta({
        report: fullReport,
        generatedBy: exportUserName,
        sort,
        timezone,
      });
      const selectedColumns = fullReport.columns.filter((column) => visibleColumnIds.includes(column.id));
      if (format === "csv" || format === "xlsx") {
        const response = await fetch("/api/reports/export", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            format,
            tab: activeTab,
            filters,
            pageSize: 5000,
            search,
            sort,
            visibleColumnIds,
            custom: activeTab === "custom" ? { ...customConfig, filters } : null,
            auditContext: {
              generatedBy: exportUserName,
              timezone,
            },
          }),
        });
        await downloadFromResponse(response, `relatorio-${activeTab}.${format}`);
        return;
      }

      if (format === "print") {
        await fetch("/api/reports/audit", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            format: "print",
            tab: activeTab,
            filters,
            search,
            sort,
            visibleColumnIds,
            custom: activeTab === "custom" ? { ...customConfig, filters } : null,
            auditContext: {
              generatedBy: exportUserName,
              timezone,
            },
          }),
        });
        const printWindow = window.open("", "_blank", "width=1200,height=800");
        if (!printWindow) return;
        printWindow.document.write(buildPrintableHtml(fullReport, audit));
        printWindow.document.close();
        printWindow.focus();
        printWindow.print();
        return;
      }

      await fetch("/api/reports/audit", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          format: "pdf",
          tab: activeTab,
          filters,
          search,
          sort,
          visibleColumnIds,
          custom: activeTab === "custom" ? { ...customConfig, filters } : null,
          auditContext: {
            generatedBy: exportUserName,
            timezone,
          },
        }),
      });
      const pdf = new jsPDF({ unit: "pt", format: "a4", orientation: "landscape" });
      renderPdfCharts(pdf, fullReport, audit);
      pdf.addPage("a4", "landscape");
      autoTable(pdf, {
        startY: PDF_MARGIN_TOP + PDF_HEADER_HEIGHT + 12,
        margin: {
          top: PDF_MARGIN_TOP + PDF_HEADER_HEIGHT + 12,
          right: PDF_MARGIN_X,
          bottom: PDF_FOOTER_HEIGHT + 16,
          left: PDF_MARGIN_X,
        },
        head: [selectedColumns.map((column) => column.label)],
        body: fullReport.rows.map((row) =>
          selectedColumns.map((column) => formatValueByType(row[column.id] ?? null, column.type))
        ),
        styles: {
          fontSize: 7.5,
          cellPadding: 4,
          lineColor: [226, 232, 240],
          lineWidth: 0.4,
          textColor: [15, 23, 42],
          valign: "middle",
        },
        headStyles: {
          fillColor: [76, 70, 182],
          textColor: 255,
          fontStyle: "bold",
          halign: "left",
        },
        alternateRowStyles: { fillColor: [248, 250, 252] },
        tableLineColor: [226, 232, 240],
        tableLineWidth: 0.4,
        showHead: "everyPage",
        rowPageBreak: "avoid",
        horizontalPageBreak: true,
        didDrawPage: () => {
          drawPdfHeader(pdf, fullReport, audit, `${fullReport.title} • Listagem detalhada`);
          drawPdfFooter(pdf);
        },
      });
      pdf.save(`relatorio-${activeTab}.pdf`);
    } finally {
      setBusyExport(null);
    }
  }

  async function savePreset() {
    setBusySave(true);
    try {
      const payload = await fetchJson<{ presets: ReportPreset[] }>("/api/reports/presets", {
        method: "POST",
        body: JSON.stringify({
          name: presetName,
          reportKey: activeTab,
          scope: presetScope,
          config: activeTab === "custom" ? { ...customConfig, filters } : filters,
        }),
      });
      setOptions((current) => (current ? { ...current, presets: payload.presets } : current));
      setSaveDialogOpen(false);
      setPresetName("");
    } finally {
      setBusySave(false);
    }
  }

  function applyPreset(presetId: string) {
    if (!options) return;
    const preset = options.presets.find((item) => item.id === presetId);
    if (!preset) return;
    setActiveTab(preset.reportKey);
    if (preset.reportKey === "custom") {
      const customPreset = preset.config as ReportCustomConfig;
      setCustomConfig({
        ...defaultCustomConfig,
        ...customPreset,
      });
      setFilters((current) => ({ ...current, ...(customPreset.filters as Record<string, string> | undefined) }));
    } else {
      const nextFilters = preset.config as Record<string, string>;
      setFilters((current) => ({ ...current, ...nextFilters }));
    }
    setPage(1);
  }

  async function deletePreset(presetId: string) {
    const payload = await fetchJson<{ presets: ReportPreset[] }>(`/api/reports/presets?id=${encodeURIComponent(presetId)}`, {
      method: "DELETE",
    });
    setOptions((current) => (current ? { ...current, presets: payload.presets } : current));
  }

  return (
    <div className="space-y-4 pb-4">
      <section className="overflow-hidden rounded-2xl border border-gray-100 bg-white shadow-none">
        <header className="flex flex-col gap-4 px-5 py-5 lg:flex-row lg:items-center lg:justify-between lg:px-6">
          <div>
            <h1 className="text-[1.95rem] font-semibold tracking-[-0.03em] text-[var(--ks-color-brand-dark)]">Relatórios</h1>
          </div>
          <div className="no-print flex flex-wrap items-center gap-2">
            <div className="relative" ref={presetMenuRef}>
              <Button
                type="button"
                variant="secondary"
                size="sm"
                icon={<FolderOpen className="h-4 w-4" />}
                iconRight={<ChevronDown className="h-4 w-4" />}
                className="rounded-xl border border-gray-100 bg-white text-slate-700 shadow-none"
                onClick={() => setPresetMenuOpen((current) => !current)}
              >
                Meus relatórios
              </Button>
              {presetMenuOpen ? (
                <div className="absolute right-0 top-11 z-20 min-w-[280px] rounded-2xl border border-gray-100 bg-white p-2 shadow-[0_14px_30px_rgba(15,23,42,0.08)]">
                  {activePresets.length > 0 ? (
                    activePresets.map((preset) => (
                      <div key={preset.id} className="flex items-center justify-between gap-3 rounded-xl px-3 py-2 hover:bg-slate-50">
                        <button
                          type="button"
                          className="min-w-0 flex-1 text-left"
                          onClick={() => {
                            setPresetMenuOpen(false);
                            applyPreset(preset.id);
                          }}
                        >
                          <div className="truncate text-sm font-medium text-slate-800">{preset.name}</div>
                          <div className="text-[11px] text-slate-500">{preset.scope === "company" ? "Empresa" : "Pessoal"}</div>
                        </button>
                        <button
                          type="button"
                          className="text-xs font-medium text-slate-500 transition hover:text-red-600"
                          onClick={() => void deletePreset(preset.id)}
                        >
                          Excluir
                        </button>
                      </div>
                    ))
                  ) : (
                    <div className="px-3 py-4 text-sm text-slate-500">Nenhum relatório salvo nesta aba.</div>
                  )}
                </div>
              ) : null}
            </div>
            <Button
              type="button"
              size="sm"
              className="rounded-xl bg-[#4f46e5] px-4 text-white"
              icon={<Plus className="h-4 w-4" />}
              onClick={() => setSaveDialogOpen(true)}
            >
              Novo relatório
            </Button>
          </div>
        </header>

        <section className="no-print border-y border-gray-100 bg-white px-5 lg:px-6">
          <div className="hidden items-center gap-8 overflow-x-auto md:flex">
            {tabs.map((tab) => (
              <button
                key={tab.key}
                type="button"
                onClick={() => void handleTabChange(tab.key)}
                className={`relative whitespace-nowrap border-b-2 py-3 text-sm font-medium transition ${
                  activeTab === tab.key
                    ? "border-[#4f46e5] text-slate-900"
                    : "border-transparent text-slate-500 hover:text-slate-700"
                }`}
              >
                {tab.label}
              </button>
            ))}
          </div>
          <div className="py-3 md:hidden">
            <CustomSelect
              value={activeTab}
              onChange={(value) => void handleTabChange(value as ReportTabKey)}
              options={tabs.map((tab) => ({ value: tab.key, label: tab.label }))}
            />
          </div>
        </section>

        {options ? (
          <section className="no-print px-5 py-4 lg:px-6">
            <div className="flex flex-wrap gap-3 xl:flex-nowrap xl:items-end">
            <div className="min-w-[260px] flex-1">
              <div className="mb-1.5 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-slate-500">
                <CalendarRange className="h-4 w-4" />
                Período
              </div>
              <div className="grid grid-cols-2 gap-2">
                <input
                  type="date"
                  value={filters.startDate}
                  onChange={(event) => updateFilter("startDate", event.target.value)}
                  className="h-10 rounded-xl border border-gray-100 bg-white px-3 text-sm text-slate-700 outline-none transition focus:border-[#4f46e5]"
                />
                <input
                  type="date"
                  value={filters.endDate}
                  onChange={(event) => updateFilter("endDate", event.target.value)}
                  className="h-10 rounded-xl border border-gray-100 bg-white px-3 text-sm text-slate-700 outline-none transition focus:border-[#4f46e5]"
                />
              </div>
            </div>

            {tabFilterFields.map((field) => (
              <div key={field.key} className="min-w-[180px] flex-1">
                <label className="mb-1.5 block text-[11px] font-semibold uppercase tracking-[0.08em] text-slate-500">
                  {field.label}
                </label>
                <select
                  value={
                    field.key === "universe"
                      ? customConfig.universe
                      : field.key === "groupBy"
                      ? customConfig.groupBy || ""
                      : field.key === "sortBy"
                        ? customConfig.sort?.columnId || ""
                        : filters[field.key] || ""
                  }
                  onChange={(event) => {
                    const value = event.target.value;
                    if (field.key === "universe") {
                      setCustomConfig((current) => ({
                        ...current,
                        universe: value as ReportCustomUniverse,
                        groupBy: null,
                        sort: null,
                      }));
                      return;
                    }
                    if (field.key === "groupBy") {
                      setCustomConfig((current) => ({ ...current, groupBy: value || null }));
                      return;
                    }
                    if (field.key === "sortBy") {
                      setCustomConfig((current) => ({
                        ...current,
                        sort: value ? { columnId: value, direction: current.sort?.direction || "desc" } : null,
                      }));
                      return;
                    }
                    updateFilter(field.key, value);
                  }}
                  className="h-10 w-full rounded-xl border border-gray-100 bg-white px-3 text-sm text-slate-700 outline-none transition focus:border-[#4f46e5]"
                >
                  {field.options.map((option) => (
                    <option key={`${field.key}-${option.value || "all"}`} value={option.value}>
                      {option.label}
                    </option>
                  ))}
                </select>
              </div>
            ))}

            <div className="flex items-end gap-2">
              <Button
                type="button"
                variant="secondary"
                size="sm"
                className="h-10 rounded-xl border border-gray-100 bg-white px-4 text-slate-700 shadow-none"
                onClick={resetFilters}
              >
                Limpar filtros
              </Button>
              <Button type="button" size="sm" className="h-10 rounded-xl bg-[#4f46e5] px-4 text-white" onClick={() => void refreshCurrentReport()}>
                Aplicar filtros
              </Button>
            </div>
          </div>

            {activeTab === "custom" ? (
              <div className="mt-4 grid grid-cols-1 gap-4 border-t border-gray-100 pt-4 xl:grid-cols-[1.35fr_0.85fr]">
              <div className="rounded-2xl border border-gray-100 bg-white p-4">
                <div className="mb-3 text-xs font-semibold uppercase tracking-[0.12em] text-[var(--ks-color-text-soft)]">
                  Colunas do relatório
                </div>
                <div className="grid grid-cols-2 gap-2 md:grid-cols-3">
                  {customUniverseFields.map((field) => {
                    const checked = customConfig.columns.includes(field.id);
                    return (
                      <label key={field.id} className="flex items-center gap-2 rounded-xl border border-gray-100 bg-white px-3 py-2">
                        <input
                          type="checkbox"
                          checked={checked}
                          onChange={(event) => {
                            setCustomConfig((current) => ({
                              ...current,
                              columns: event.target.checked
                                ? [...current.columns, field.id]
                                : current.columns.length > 1
                                  ? current.columns.filter((columnId) => columnId !== field.id)
                                  : current.columns,
                            }));
                          }}
                        />
                        <span className="text-sm text-[var(--ks-color-text-primary)]">{field.label}</span>
                      </label>
                    );
                  })}
                </div>
                <div className="mb-3 mt-5 text-xs font-semibold uppercase tracking-[0.12em] text-[var(--ks-color-text-soft)]">
                  Indicadores do painel
                </div>
                <div className="grid grid-cols-1 gap-2 md:grid-cols-2">
                  {customUniverseMetrics.map((metric) => {
                    const checked = customConfig.metrics.includes(metric.id);
                    return (
                      <label key={metric.id} className="flex items-center gap-2 rounded-xl border border-gray-100 bg-white px-3 py-2">
                        <input
                          type="checkbox"
                          checked={checked}
                          onChange={(event) => {
                            setCustomConfig((current) => ({
                              ...current,
                              metrics: event.target.checked
                                ? [...current.metrics, metric.id]
                                : current.metrics.length > 1
                                  ? current.metrics.filter((metricId) => metricId !== metric.id)
                                  : current.metrics,
                            }));
                          }}
                        />
                        <span className="text-sm text-[var(--ks-color-text-primary)]">{metric.label}</span>
                      </label>
                    );
                  })}
                </div>
              </div>

              <div className="rounded-2xl border border-gray-100 bg-white p-4">
                <div className="mb-3 text-xs font-semibold uppercase tracking-[0.12em] text-[var(--ks-color-text-soft)]">
                  Modelos salvos
                </div>
                <div className="space-y-2">
                  {activePresets.length > 0 ? (
                    activePresets.map((preset) => (
                      <div key={preset.id} className="flex items-center justify-between gap-3 rounded-xl border border-gray-100 bg-white px-3 py-2">
                        <div className="min-w-0">
                          <div className="truncate text-sm font-medium text-[var(--ks-color-text-primary)]">{preset.name}</div>
                          <div className="text-[11px] text-[var(--ks-color-text-soft)]">{preset.scope === "company" ? "Empresa" : "Pessoal"}</div>
                        </div>
                        <div className="flex items-center gap-2">
                          <Button type="button" size="sm" variant="secondary" className="rounded-lg" onClick={() => applyPreset(preset.id)}>
                            Usar
                          </Button>
                          <Button type="button" size="sm" variant="ghost" className="rounded-lg" onClick={() => void deletePreset(preset.id)}>
                            Excluir
                          </Button>
                        </div>
                      </div>
                    ))
                  ) : (
                    <div className="rounded-xl border border-dashed border-gray-200 px-3 py-6 text-center text-sm text-[var(--ks-color-text-muted)]">
                      Nenhum modelo personalizado salvo.
                    </div>
                  )}
                </div>
              </div>
              </div>
            ) : null}
          </section>
        ) : null}
      </section>

      {report ? (
        <>
          <section className="grid grid-cols-1 gap-3 xl:grid-cols-4">
            {kpiCards.map((kpi) => {
              const Icon = kpi.presentation.icon;
              return (
                <article
                  key={kpi.id}
                  className="flex min-h-[146px] items-start gap-4 rounded-2xl border border-gray-100 bg-white px-4 py-4 shadow-none"
                >
                  <div className={`flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl ${kpi.presentation.accentClass}`}>
                    <Icon className="h-5 w-5" />
                  </div>
                  <div className="min-w-0 flex-1">
                    <div className="text-[11px] font-semibold uppercase tracking-[0.08em] text-slate-500">{kpi.label}</div>
                    <div className="mt-1 text-[1.95rem] font-semibold tracking-[-0.035em] text-slate-900">
                      {formatValueByType(kpi.value, kpi.type === "currency" ? "currency" : kpi.type === "percent" ? "percent" : "number")}
                    </div>
                    <div className="mt-3 flex items-center gap-2">
                      <span className={`inline-flex rounded-full px-2 py-1 text-[11px] font-semibold ${getVariationClass(kpi.variation)}`}>
                        {formatVariation(kpi.variation)}
                      </span>
                      <span className="text-xs text-slate-500">vs período anterior</span>
                    </div>
                  </div>
                </article>
              );
            })}
          </section>

          <ReportCharts charts={report.charts} comparisonCharts={alignedComparisonCharts} />

          <ReportDataTable
            columns={report.columns}
            rows={report.rows.map((row) => {
              const normalized = { ...row };
              for (const column of report.columns) {
                if (column.type === "badge") {
                  normalized[column.id] = row[column.id] ? String(row[column.id]) : "—";
                }
              }
              return normalized;
            })}
            totalRows={report.totalRows}
            page={report.page}
            pageSize={report.pageSize}
            sort={sort}
            visibleColumnIds={visibleColumnIds}
            search={search}
            onSearchChange={(value) => {
              setSearch(value);
              setPage(1);
            }}
            onPageChange={setPage}
            onSortChange={(nextSort) => {
              setSort(nextSort);
              setPage(1);
            }}
            onVisibleColumnIdsChange={setVisibleColumnIds}
            onExport={(format) => void exportReport(format)}
            busyExport={busyExport}
          />
        </>
      ) : null}

      {loading && !report ? (
        <section className="ks-surface border border-[rgba(226,232,240,0.9)] p-8 text-center text-sm text-[var(--ks-color-text-muted)]">
          Carregando relatórios...
        </section>
      ) : null}

      {!loading && falha ? (
        <section className="ks-surface border border-[rgba(226,232,240,0.9)] p-6">
          <div className="flex items-start gap-3">
            <TriangleAlert className="mt-0.5 h-5 w-5 shrink-0 text-[var(--ks-color-status-danger)]" />
            <div className="min-w-0">
              <p className="text-sm font-semibold text-[var(--ks-color-text-strong)]">
                Não foi possível gerar este relatório.
              </p>
              <p className="mt-1 text-sm text-[var(--ks-color-text-muted)]">{falha}</p>
              <button
                type="button"
                onClick={() => void loadReport()}
                className="ks-button ks-button-secondary ks-button-sm mt-3"
              >
                Tentar de novo
              </button>
            </div>
          </div>
        </section>
      ) : null}

      <Dialog open={saveDialogOpen} onOpenChange={setSaveDialogOpen}>
        <DialogContent className="max-w-xl rounded-2xl border-none p-0">
          <div className="p-6">
            <DialogHeader>
              <DialogTitle>Salvar modelo</DialogTitle>
              <DialogDescription>
                Salve a configuração atual para reutilizar o relatório com os mesmos filtros e colunas.
              </DialogDescription>
            </DialogHeader>

            <div className="mt-5 grid grid-cols-1 gap-4">
              <Input label="Nome do modelo" value={presetName} onChange={(event) => setPresetName(event.target.value)} placeholder="Ex.: Relatório Comercial João" />
              <CustomSelect
                label="Escopo"
                value={presetScope}
                onChange={(value) => setPresetScope(value as "company" | "user")}
                options={[
                  { value: "company", label: "Empresa" },
                  { value: "user", label: "Pessoal" },
                ]}
              />
            </div>

            <DialogFooter className="mt-6">
              <Button type="button" variant="secondary" onClick={() => setSaveDialogOpen(false)}>
                Cancelar
              </Button>
              <Button type="button" onClick={() => void savePreset()} disabled={!presetName.trim() || busySave}>
                Salvar
              </Button>
            </DialogFooter>
          </div>
        </DialogContent>
      </Dialog>
    </div>
  );
}
