"use client";

import { useState, type ChangeEvent, type FocusEvent, type InputHTMLAttributes } from "react";
import { Input, type InputProps } from "@/components/ui/input";
import { numeroDoTexto, textoDecimalValido, textoDoNumero, textoInteiroValido } from "@/lib/numero-do-campo";

export { numeroDoTexto, textoDecimalValido, textoDoNumero, textoInteiroValido };

/**
 * Campo de número decimal que respeita quem digita.
 *
 * O jeito antigo convertia o texto em número a cada tecla: "0," virava 0, a
 * vírgula sumia e ninguém conseguia escrever 0,160. Aqui o texto fica como a
 * pessoa digitou enquanto ela digita; o número lido sobe para o pai a cada
 * mudança; e o texto só é reescrito quando o valor muda por fora (uma receita
 * que chega pronta, uma perda recalculada pela bobina).
 *
 * Aceita vírgula ou ponto como separador decimal e não aceita separador de
 * milhar: o que aparece no campo é exatamente o que vai ser lido.
 */

type Opcoes = {
  /** Mostra o zero como campo vazio (com o placeholder aparecendo). */
  zeroVazio?: boolean;
  permiteNegativo?: boolean;
  /** Máximo de casas decimais aceitas; 0 vira um campo de inteiros. */
  casas?: number;
};

function useTextoDecimal(
  valor: number | null | undefined,
  aoMudar: (n: number) => void,
  { zeroVazio = false, permiteNegativo = false, casas }: Opcoes
) {
  const [texto, setTexto] = useState(() => textoDoNumero(valor, zeroVazio));
  /* O último número que este campo conhece: o que emitiu ao digitar ou o que
     recebeu de fora. Quando o valor do pai diverge dele, veio de fora, e o
     texto é reescrito durante a própria renderização (o padrão do React para
     ajustar estado quando uma prop muda, sem efeito). */
  const [conhecido, setConhecido] = useState(() => numeroDoTexto(valor));
  const deFora = numeroDoTexto(valor);
  if (deFora !== conhecido) {
    setConhecido(deFora);
    setTexto(textoDoNumero(valor, zeroVazio));
  }

  const onChange = (e: ChangeEvent<HTMLInputElement>) => {
    const t = e.target.value.replace(/\s/g, "");
    if (casas === 0 ? !textoInteiroValido(t) : !textoDecimalValido(t, permiteNegativo)) return;
    if (casas !== undefined && casas > 0) {
      const decimais = t.split(/[.,]/)[1];
      if (decimais && decimais.length > casas) return;
    }
    const n = numeroDoTexto(t);
    setTexto(t);
    setConhecido(n);
    aoMudar(n);
  };

  /* "5," ou "-" sozinhos ao sair do campo: mostra o número que valeu. */
  const aoSair = () => {
    if (/[.,]$/.test(texto) || texto === "-") setTexto(textoDoNumero(numeroDoTexto(texto), zeroVazio));
  };

  return { texto, onChange, aoSair };
}

type PropsBase = Opcoes & {
  valor: number | null | undefined;
  aoMudar: (n: number) => void;
};

/** Um <input> cru: recebe a classe (ks-field, ks-input…) de quem usa. */
export function CampoDecimal({
  valor, aoMudar, zeroVazio, permiteNegativo, casas, onBlur, ...resto
}: PropsBase & Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "type" | "inputMode">) {
  const { texto, onChange, aoSair } = useTextoDecimal(valor, aoMudar, { zeroVazio, permiteNegativo, casas });
  return (
    <input
      {...resto}
      type="text"
      inputMode={casas === 0 ? "numeric" : "decimal"}
      autoComplete="off"
      value={texto}
      onChange={onChange}
      onBlur={(e: FocusEvent<HTMLInputElement>) => { aoSair(); onBlur?.(e); }}
    />
  );
}

/** O mesmo campo vestido com o Input da casa (rótulo, ícone, sufixo). */
export function EntradaDecimal({
  valor, aoMudar, zeroVazio, permiteNegativo, casas, onBlur, ...resto
}: PropsBase & Omit<InputProps, "value" | "onChange" | "type" | "inputMode">) {
  const { texto, onChange, aoSair } = useTextoDecimal(valor, aoMudar, { zeroVazio, permiteNegativo, casas });
  return (
    <Input
      {...resto}
      type="text"
      inputMode={casas === 0 ? "numeric" : "decimal"}
      autoComplete="off"
      value={texto}
      onChange={onChange}
      onBlur={(e: FocusEvent<HTMLInputElement>) => { aoSair(); onBlur?.(e); }}
    />
  );
}
