import { TaxCatalogProvider } from "@erp/core";
import React from "react";
import { UseFormReturn } from "react-hook-form";
import {
  InvoiceItemCalcResult,
  calculateInvoiceHeaderAmounts,
  calculateInvoiceItemAmounts,
} 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).
 */
export function useInvoiceAutoRecalc(
  form: UseFormReturn<InvoiceFormData>,
  params: UseInvoiceAutoRecalcParams
) {
  const {
    watch,
    trigger,
    formState: { isDirty, isLoading, isSubmitting },
  } = form;

  const { currency_code, taxCatalog } = params;

  // Cache de los totales de cada línea de factura
  const itemCache = React.useRef<Map<number, ReturnType<typeof calculateInvoiceItemAmounts>>>(
    new Map()
  );

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

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

  // Totales globales (usa funciones del dominio)
  const calculateInvoiceTotals = React.useCallback(
    (items: InvoiceItemFormData[], header_discount_percentage: number) => {
      const lines = items
        .filter((i) => !i.is_non_valued)
        .map((i) => {
          const itemTotals = calculateItemTotals(i, header_discount_percentage);
          return {
            subtotal_amount: itemTotals.subtotal_amount,
            discount_amount: itemTotals.discount_amount,
            header_discount_amount: itemTotals.header_discount_amount,
            taxable_amount: itemTotals.taxable_amount,
            taxes_amount: itemTotals.taxes_amount,
            total_amount: itemTotals.total_amount,
            taxes_summary: itemTotals.taxes_summary,
          };
        });

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

  // Observamos el formulario esperando cualquier cambio
  React.useEffect(() => {
    if (!isDirty || isLoading || isSubmitting) return;

    const subscription = watch((formData, { name, type }) => {
      const items = (formData?.items || []) as InvoiceItemFormData[];
      const header_discount_percentage = formData?.discount_percentage || 0;

      if (items.length === 0) return;

      // Detectar cambios en la cabecera
      if (name === "discount_percentage") {
        // Recalcular totales de factura
        const invoiceTotals = calculateInvoiceTotals(items, header_discount_percentage);

        // Estableer valores en cabecera
        setInvoiceTotals(form, invoiceTotals);

        // Forzar actualización de todas las líneas
        items.forEach((item, idx) => {
          const newTotals = calculateItemTotals(item, header_discount_percentage);
          itemCache.current.set(idx, newTotals);
          setInvoiceItemTotals(form, idx, newTotals);
        });

        trigger([
          "subtotal_amount",
          "discount_amount",
          "taxable_amount",
          "taxes_amount",
          "total_amount",
        ]);
      }

      // 2. Cambio puntual de una línea
      if (name?.startsWith("items.") && type === "change") {
        const index = Number(name.split(".")[1]);
        const field = name.split(".")[2];

        if (["quantity", "unit_amount", "discount_percentage", "tax_codes"].includes(field)) {
          const item = items[index] as InvoiceItemFormData;
          const prevTotals = itemCache.current.get(index);
          const newTotals = calculateItemTotals(item, header_discount_percentage);

          // Si no hay cambios en los totales, no tocamos nada
          const itemHasChanges =
            !prevTotals || JSON.stringify(prevTotals) !== JSON.stringify(newTotals);

          if (!itemHasChanges) {
            return;
          }

          // El total de esta línea ha cambiado => actualizamos la cache
          itemCache.current.set(index, newTotals);

          // Actualizar los campos de esta línea
          setInvoiceItemTotals(form, index, newTotals);

          // Recalcular totales de factura
          const invoiceTotals = calculateInvoiceTotals(items, header_discount_percentage);

          // Estableer valores en cabecera
          setInvoiceTotals(form, invoiceTotals);

          trigger([
            "items",
            "subtotal_amount",
            "discount_amount",
            "taxable_amount",
            "taxes_amount",
            "total_amount",
          ]);
        }
      }
    });

    return () => subscription.unsubscribe();
  }, [
    form,
    watch,
    trigger,
    isDirty,
    isLoading,
    isSubmitting,
    calculateItemTotals,
    calculateInvoiceTotals,
  ]);
}

// Ayudante para rellenar los importes de una línea
function setInvoiceItemTotals(
  form: UseFormReturn<InvoiceFormData>,
  index: number,
  newTotals: InvoiceItemCalcResult
) {
  const { setValue } = form;

  setValue(`items.${index}.subtotal_amount`, newTotals.subtotal_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
  setValue(`items.${index}.discount_amount`, newTotals.discount_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
  setValue(`items.${index}.taxable_amount`, newTotals.taxable_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
  setValue(`items.${index}.taxes_amount`, newTotals.taxes_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
  setValue(`items.${index}.total_amount`, newTotals.total_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
}

// Ayudante para actualizar los importes de la cabecera
function setInvoiceTotals(
  form: UseFormReturn<InvoiceFormData>,
  invoiceTotals: ReturnType<typeof calculateInvoiceHeaderAmounts>
) {
  const { setValue } = form;

  setValue("subtotal_amount", invoiceTotals.subtotal_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
  setValue("discount_amount", invoiceTotals.header_discount_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
  setValue("taxable_amount", invoiceTotals.taxable_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
  setValue("taxes_amount", invoiceTotals.taxes_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });
  setValue("total_amount", invoiceTotals.total_amount, {
    shouldDirty: true,
    shouldValidate: false,
  });

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