import { TaxCatalogProvider } from "@erp/core";
import React from "react";
import { UseFormReturn, useWatch } from "react-hook-form";
import {
  calculateInvoiceHeaderAmounts,
  calculateInvoiceItemAmounts,
  InvoiceItemCalcResult,
} from "../../domain";
import { InvoiceFormData, InvoiceItemFormData } from "../../schemas";

export type UseInvoiceAutoRecalcParams = {
  currency_code: string;
  taxCatalog: TaxCatalogProvider;
};

/**
 * Hook que recalcula automáticamente los totales de cada línea
 * y los totales generales de la factura cuando cambian los valores relevantes.
 * Adaptado a formulario con números planos (no DTOs).
 * Evita renders innecesarios (debounce + useDeferredValue).
 */
export function useInvoiceAutoRecalc(
  form: UseFormReturn<InvoiceFormData>,
  { currency_code, taxCatalog }: UseInvoiceAutoRecalcParams
) {
  const { setValue, trigger, control } = form;

  // Observa los ítems y el descuento global
  const watchedItems = useWatch({ control, name: "items" }) ?? [];
  const watchedDiscount = useWatch({ control, name: "discount_percentage" }) ?? 0;

  // Diferir valores pesados para reducir renders (React 19)
  const deferredItems = React.useDeferredValue(watchedItems);
  const deferredDiscount = React.useDeferredValue(watchedDiscount);

  // Cache para evitar recálculos redundantes
  const itemCache = React.useRef<Map<number, InvoiceItemCalcResult>>(new Map());

  // Debounce para agrupar recalculados rápidos
  const debounceTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null);

  // Cálculo de una línea individual
  const calculateItemTotals = React.useCallback(
    (item: InvoiceItemFormData, header_discount_percentage: number) => {
      const sanitize = (v?: number | string) => (v && !Number.isNaN(Number(v)) ? String(v) : "0");

      return calculateInvoiceItemAmounts(
        {
          quantity: sanitize(item.quantity),
          unit_amount: sanitize(item.unit_amount),
          discount_percentage: sanitize(item.discount_percentage),
          header_discount_percentage: sanitize(header_discount_percentage),
          tax_codes: item.tax_codes,
        },
        currency_code,
        taxCatalog
      );
    },
    [taxCatalog, currency_code]
  );

  // Cálculo global de factura
  const calculateInvoiceTotals = React.useCallback(
    (items: InvoiceItemFormData[], header_discount_percentage: number) => {
      const lines = items
        .filter((i) => !i.is_non_valued)
        .map((i) => {
          const totals = calculateItemTotals(i, header_discount_percentage);
          return {
            subtotal_amount: totals.subtotal_amount,
            discount_amount: totals.discount_amount,
            header_discount_amount: totals.header_discount_amount,
            taxable_amount: totals.taxable_amount,
            taxes_amount: totals.taxes_amount,
            total_amount: totals.total_amount,
            taxes_summary: totals.taxes_summary,
          };
        });

      return calculateInvoiceHeaderAmounts(lines, currency_code);
    },
    [calculateItemTotals, currency_code]
  );

  // Observamos el formulario esperando cualquier cambio
  React.useEffect(() => {
    if (!deferredItems.length) return;

    if (debounceTimer.current) clearTimeout(debounceTimer.current);

    debounceTimer.current = setTimeout(() => {
      let shouldUpdateHeader = false;

      deferredItems.forEach((item, idx) => {
        const prev = itemCache.current.get(idx);
        const next = calculateItemTotals(item, deferredDiscount);

        if (!prev || JSON.stringify(prev) !== JSON.stringify(next)) {
          shouldUpdateHeader = true;
          itemCache.current.set(idx, next);
          setInvoiceItemTotals(form, idx, next);
        }
      });

      if (shouldUpdateHeader) {
        const totals = calculateInvoiceTotals(deferredItems, deferredDiscount);
        setInvoiceTotals(form, totals);

        void trigger([
          "subtotal_amount",
          "discount_amount",
          "taxable_amount",
          "taxes_amount",
          "total_amount",
        ]);
      }
    }, 100); // <-- debounce de 100ms, ajustable

    return () => {
      if (debounceTimer.current) clearTimeout(debounceTimer.current);
    };
  }, [deferredItems, deferredDiscount, calculateItemTotals, calculateInvoiceTotals, form, trigger]);
}

// Ayudante para rellenar los importes de una línea
function setInvoiceItemTotals(
  form: UseFormReturn<InvoiceFormData>,
  index: number,
  totals: InvoiceItemCalcResult
) {
  const { setValue } = form;
  const opts = { shouldDirty: true, shouldValidate: false } as const;

  setValue(`items.${index}.subtotal_amount`, totals.subtotal_amount, opts);
  setValue(`items.${index}.discount_amount`, totals.discount_amount, opts);
  setValue(`items.${index}.taxable_amount`, totals.taxable_amount, opts);
  setValue(`items.${index}.taxes_amount`, totals.taxes_amount, opts);
  setValue(`items.${index}.total_amount`, totals.total_amount, opts);
}

// Ayudante para actualizar los importes de la cabecera
function setInvoiceTotals(
  form: UseFormReturn<InvoiceFormData>,
  totals: ReturnType<typeof calculateInvoiceHeaderAmounts>
) {
  const { setValue } = form;
  const opts = { shouldDirty: true, shouldValidate: false } as const;

  setValue("subtotal_amount", totals.subtotal_amount, opts);
  setValue("discount_amount", totals.header_discount_amount, opts);
  setValue("taxable_amount", totals.taxable_amount, opts);
  setValue("taxes_amount", totals.taxes_amount, opts);
  setValue("total_amount", totals.total_amount, opts);

  setValue(
    "taxes",
    totals.taxes_summary.map((t) => ({
      tax_code: t.code,
      tax_label: t.name,
      taxable_amount: t.taxable_amount,
      taxes_amount: t.taxes_amount,
    })),
    opts
  );
}
