import * as React from "react";
import { QuantityDTO } from "../../common";

/**
 * Hook para manipular cantidades escaladas (value+scale).
 * Ejemplo: { value:"1500", scale:"2" } → 15.00 unidades
 */
export const isEmptyQuantityDTO = (q?: QuantityDTO | null) =>
  !q || q.value.trim() === "" || q.scale.trim() === "";

// Redondeo a escala (por defecto 2)
const roundToScale = (n: number, scale = 2) => {
  const f = 10 ** scale;
  return Math.round(n * f) / f;
};

// Quita caracteres no numéricos salvo signos y separadores
const stripNumberish = (s: string) => s.replace(/[^\d.,\-]/g, "").trim();

// Parse tolerante: “1.234,5” | “1,234.5” | “1234.5” → número JS
const parseLocaleNumber = (raw: string): number | null => {
  if (!raw) return null;
  const s = stripNumberish(raw);
  if (!s) return null;
  const lastComma = s.lastIndexOf(",");
  const lastDot = s.lastIndexOf(".");
  let normalized = s;
  if (lastComma > -1 && lastDot > -1) {
    if (lastComma > lastDot) normalized = s.replace(/\./g, "").replace(",", ".");
    else normalized = s.replace(/,/g, "");
  } else if (lastComma > -1) normalized = s.replace(",", ".");
  const n = Number(normalized);
  return Number.isFinite(n) ? n : null;
};

export function useQuantity(overrides?: {
  defaultScale?: number; // por defecto 2
  min?: number; // clamp opcional (ej. 0)
  max?: number; // clamp opcional
}) {
  const defaultScale = overrides?.defaultScale ?? 2;
  const min = overrides?.min;
  const max = overrides?.max;

  // DTO → número (ej. {100, "2"} → 1)
  const toNumber = React.useCallback(
    (q?: QuantityDTO | null): number => {
      if (isEmptyQuantityDTO(q)) return 0;
      const scale = Number(q!.scale || defaultScale);
      return Number(q!.value || 0) / 10 ** scale;
    },
    [defaultScale]
  );

  // número → DTO (manteniendo escala deseada)
  const fromNumber = React.useCallback(
    (n: number, scale: number = defaultScale): QuantityDTO => ({
      value: String(Math.round(n * 10 ** scale)),
      scale: String(scale),
    }),
    [defaultScale]
  );

  // Reescala manteniendo magnitud
  const withScale = React.useCallback(
    (q: QuantityDTO, scale: number) => {
      const curr = toNumber(q);
      return fromNumber(curr, scale);
    },
    [toNumber, fromNumber]
  );

  // Formateo sin relleno de ceros (máx. escala)
  const formatPlain = React.useCallback(
    (q: QuantityDTO) => {
      const n = toNumber(q);
      const dec = Number(q.scale || defaultScale);
      return new Intl.NumberFormat(undefined, {
        maximumFractionDigits: dec,
        minimumFractionDigits: Number.isInteger(n) ? 0 : 0,
        useGrouping: false,
      }).format(n);
    },
    [toNumber, defaultScale]
  );

  // Parse texto → número (tolerante ,/.)
  const parse = React.useCallback((text: string): number | null => parseLocaleNumber(text), []);

  // DTO vacío ↔ null (adaptadores)
  const fromApi = React.useCallback(
    (q?: QuantityDTO | null): QuantityDTO | null => (q && !isEmptyQuantityDTO(q) ? q : null),
    []
  );
  const toApi = React.useCallback(
    (q: QuantityDTO | null): QuantityDTO => (q ? q : { value: "", scale: "" }),
    []
  );

  // Operaciones aritméticas simples (misma escala resultado)
  const add = React.useCallback(
    (a: QuantityDTO, b: QuantityDTO): QuantityDTO => {
      const scale = Math.max(Number(a.scale || defaultScale), Number(b.scale || defaultScale));
      const av = withScale(a, scale);
      const bv = withScale(b, scale);
      return { value: String(Number(av.value) + Number(bv.value)), scale: String(scale) };
    },
    [withScale, defaultScale]
  );

  const sub = React.useCallback(
    (a: QuantityDTO, b: QuantityDTO): QuantityDTO => {
      const scale = Math.max(Number(a.scale || defaultScale), Number(b.scale || defaultScale));
      const av = withScale(a, scale);
      const bv = withScale(b, scale);
      return { value: String(Number(av.value) - Number(bv.value)), scale: String(scale) };
    },
    [withScale, defaultScale]
  );

  const multiply = React.useCallback(
    (q: QuantityDTO, k: number, outScale?: number): QuantityDTO => {
      const sc = outScale ?? Number(q.scale || defaultScale);
      const n = toNumber(q) * k;
      return fromNumber(roundToScale(n, sc), sc);
    },
    [toNumber, fromNumber, defaultScale]
  );

  // Stepping teclado (p.ej. ArrowUp/Down)
  const stepNumber = React.useCallback(
    (base: number, step = 1, scale: number = defaultScale) => {
      let next = base + step;
      if (min !== undefined) next = Math.max(next, min);
      if (max !== undefined) next = Math.min(next, max);
      return roundToScale(next, scale);
    },
    [defaultScale, min, max]
  );

  // Estado/ayudas
  const clamp = React.useCallback(
    (n: number, s: number = defaultScale) => {
      let v = n;
      if (min !== undefined) v = Math.max(v, min);
      if (max !== undefined) v = Math.min(v, max);
      return roundToScale(v, s);
    },
    [min, max, defaultScale]
  );

  const isZero = React.useCallback((q?: QuantityDTO | null) => toNumber(q) === 0, [toNumber]);

  return React.useMemo(
    () => ({
      // Conversión
      toNumber,
      fromNumber,
      withScale,

      // Formateo/parseo
      formatPlain,
      parse,

      // DTO vacío / adaptadores
      isEmptyQuantityDTO,
      fromApi,
      toApi,

      // Operaciones
      add,
      sub,
      multiply,

      // Teclado/estado
      stepNumber,
      roundToScale,
      clamp,
      isZero,

      // Utils UI
      stripNumberish,

      // Config efectiva
      defaultScale,
      min,
      max,
    }),
    [
      toNumber,
      fromNumber,
      withScale,
      formatPlain,
      parse,
      add,
      sub,
      multiply,
      stepNumber,
      clamp,
      isZero,
      defaultScale,
      min,
      max,
    ]
  );
}
