"use client";

import { useEffect, useId, useRef, type ReactNode } from "react";
import { X } from "lucide-react";

/* ------------------------------------------------------------------ */
/* Formatos                                                            */
/* ------------------------------------------------------------------ */

export function moeda(valor: number, casas = 2) {
  return new Intl.NumberFormat("pt-BR", {
    style: "currency",
    currency: "BRL",
    minimumFractionDigits: casas,
    maximumFractionDigits: casas,
  }).format(Number.isFinite(valor) ? valor : 0);
}

export function inteiro(valor: number) {
  return new Intl.NumberFormat("pt-BR").format(Number.isFinite(valor) ? valor : 0);
}

export function plural(n: number, um: string, varios: string) {
  return `${inteiro(n)} ${Math.abs(n) === 1 ? um : varios}`;
}

export function dataEHora(valor: string | null | undefined) {
  if (!valor) return "";
  const data = new Date(valor);
  if (Number.isNaN(data.getTime())) return "";
  return data.toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" });
}

export function haQuantoTempo(valor: string | null | undefined, agora = Date.now()) {
  if (!valor) return "";
  const quando = new Date(valor).getTime();
  if (!Number.isFinite(quando)) return "";
  const minutos = Math.round((agora - quando) / 60000);
  if (minutos < 1) return "agora há pouco";
  if (minutos < 60) return `há ${minutos} min`;
  const horas = Math.round(minutos / 60);
  if (horas < 24) return `há ${horas} h`;
  const dias = Math.round(horas / 24);
  return `há ${plural(dias, "dia", "dias")}`;
}

export function mesPorExtenso(competencia: string) {
  const [ano, mes] = competencia.split("-").map(Number);
  if (!ano || !mes) return competencia;
  const nome = new Date(Date.UTC(ano, mes - 1, 15)).toLocaleDateString("pt-BR", { month: "long", timeZone: "UTC" });
  return `${nome} de ${ano}`;
}

export function cnpjFormatado(valor: string) {
  const d = String(valor || "").replace(/\D/g, "");
  if (d.length !== 14) return valor;
  return `${d.slice(0, 2)}.${d.slice(2, 5)}.${d.slice(5, 8)}/${d.slice(8, 12)}-${d.slice(12)}`;
}

/** Mesmo entendimento do servidor: com vírgula, ela é o decimal e o ponto é milhar. */
export function decimalDoCampo(valor: string) {
  const limpo = String(valor ?? "").trim().replace(/\s/g, "").replace(/^R\$/i, "");
  if (!limpo) return Number.NaN;
  const normalizado = limpo.includes(",") ? limpo.replace(/\./g, "").replace(",", ".") : limpo;
  return Number(normalizado);
}

export function decimalParaCampo(valor: number, casas = 2) {
  if (!Number.isFinite(valor)) return "";
  return valor.toLocaleString("pt-BR", { minimumFractionDigits: casas, maximumFractionDigits: Math.max(casas, 4), useGrouping: false });
}

/* ------------------------------------------------------------------ */
/* Pedidos ao servidor                                                 */
/* ------------------------------------------------------------------ */

export async function pedir<T = { ok: true }>(url: string, init?: RequestInit): Promise<T> {
  let resposta: Response;
  try {
    resposta = await fetch(url, {
      ...init,
      cache: "no-store",
      headers: init?.body ? { "Content-Type": "application/json", ...(init.headers || {}) } : init?.headers,
    });
  } catch {
    throw new Error("Sem conexão com o servidor. Confira a internet e tente de novo.");
  }
  const corpo = (await resposta.json().catch(() => null)) as (T & { error?: string }) | null;
  if (!resposta.ok) throw new Error(corpo?.error || "Não foi possível concluir agora.");
  return corpo as T;
}

/* ------------------------------------------------------------------ */
/* Peças                                                               */
/* ------------------------------------------------------------------ */

export type Tom = "verde" | "ambar" | "vermelho" | "cinza" | "violeta" | "azul";

const TONS: Record<Tom, string> = {
  verde: "border-emerald-200 bg-emerald-50 text-emerald-700",
  ambar: "border-amber-200 bg-amber-50 text-amber-800",
  vermelho: "border-rose-200 bg-rose-50 text-rose-700",
  cinza: "border-slate-200 bg-slate-50 text-slate-600",
  violeta: "border-violet-200 bg-violet-50 text-violet-700",
  azul: "border-sky-200 bg-sky-50 text-sky-700",
};

export function Selo({ tom, children, titulo }: { tom: Tom; children: ReactNode; titulo?: string }) {
  return (
    <span
      title={titulo}
      className={`inline-flex items-center gap-1 whitespace-nowrap rounded-full border px-2.5 py-0.5 text-[11px] font-semibold ${TONS[tom]}`}
    >
      {children}
    </span>
  );
}

export function Barra({ valor, maximo, tom }: { valor: number; maximo: number; tom?: "auto" | Tom }) {
  const fracao = maximo > 0 ? valor / maximo : valor > 0 ? 1 : 0;
  const largura = `${Math.min(100, Math.max(0, fracao * 100))}%`;
  const cor =
    tom && tom !== "auto"
      ? { verde: "bg-emerald-500", ambar: "bg-amber-500", vermelho: "bg-rose-500", cinza: "bg-slate-400", violeta: "bg-violet-500", azul: "bg-sky-500" }[tom]
      : fracao > 1
        ? "bg-rose-500"
        : fracao >= 0.8
          ? "bg-amber-500"
          : "bg-emerald-500";
  return (
    <div
      className="h-2 w-full overflow-hidden rounded-full bg-slate-100"
      role="progressbar"
      aria-valuemin={0}
      aria-valuemax={maximo}
      aria-valuenow={valor}
    >
      <div className={`h-full rounded-full transition-[width] duration-500 ${cor}`} style={{ width: largura }} />
    </div>
  );
}

export function Interruptor({
  ligado,
  aoMudar,
  rotulo,
  desabilitado,
}: {
  ligado: boolean;
  aoMudar: (ligado: boolean) => void;
  rotulo: string;
  desabilitado?: boolean;
}) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={ligado}
      aria-label={rotulo}
      disabled={desabilitado}
      onClick={() => aoMudar(!ligado)}
      className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 disabled:opacity-50 ${
        ligado ? "bg-emerald-500" : "bg-slate-300"
      }`}
    >
      <span className={`inline-block h-5 w-5 rounded-full bg-white shadow transition ${ligado ? "translate-x-[22px]" : "translate-x-0.5"}`} />
    </button>
  );
}

export function Campo({
  rotulo,
  ajuda,
  erro,
  children,
}: {
  rotulo: string;
  ajuda?: ReactNode;
  erro?: string | null;
  children: (id: string) => ReactNode;
}) {
  const id = useId();
  return (
    <div className="flex flex-col gap-1.5">
      <label htmlFor={id} className="text-sm font-medium text-slate-800">
        {rotulo}
      </label>
      {children(id)}
      {erro ? <p className="text-xs font-medium text-rose-600">{erro}</p> : ajuda ? <p className="text-xs leading-5 text-slate-500">{ajuda}</p> : null}
    </div>
  );
}

export const CLASSE_DO_CAMPO =
  "h-11 w-full rounded-xl border border-slate-200 bg-white px-3.5 text-sm text-slate-900 shadow-sm outline-none transition placeholder:text-slate-400 focus:border-slate-400 focus:ring-4 focus:ring-slate-100 disabled:bg-slate-50";

export const BOTAO_PRIMARIO =
  "inline-flex h-11 items-center justify-center gap-2 rounded-xl bg-slate-900 px-5 text-sm font-semibold text-white shadow-sm transition hover:bg-slate-800 active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50";

export const BOTAO_SECUNDARIO =
  "inline-flex h-10 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 transition hover:bg-slate-50 hover:text-slate-900 disabled:pointer-events-none disabled:opacity-50";

export function Modal({
  aberto,
  aoFechar,
  titulo,
  descricao,
  children,
  rodape,
  largura = "max-w-lg",
}: {
  aberto: boolean;
  aoFechar: () => void;
  titulo: string;
  descricao?: ReactNode;
  children?: ReactNode;
  rodape?: ReactNode;
  largura?: string;
}) {
  const idDoTitulo = useId();
  /* A função de fechar muda a cada render de quem usa o modal; o Escape lê sempre a última. */
  const fechar = useRef(aoFechar);
  useEffect(() => {
    fechar.current = aoFechar;
  });

  useEffect(() => {
    if (!aberto) return;
    const aoTeclar = (evento: KeyboardEvent) => {
      if (evento.key === "Escape") fechar.current();
    };
    window.addEventListener("keydown", aoTeclar);
    const antes = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", aoTeclar);
      document.body.style.overflow = antes;
    };
  }, [aberto]);

  if (!aberto) return null;
  return (
    <div className="fixed inset-0 z-50 grid place-items-center bg-slate-900/50 p-4" onMouseDown={aoFechar}>
      <div
        role="dialog"
        aria-modal="true"
        aria-labelledby={idDoTitulo}
        className={`flex max-h-[calc(100vh-2rem)] w-full ${largura} flex-col overflow-hidden rounded-2xl bg-white shadow-xl`}
        onMouseDown={(evento) => evento.stopPropagation()}
      >
        <div className="flex items-start justify-between gap-4 border-b border-slate-100 px-6 py-5">
          <div className="min-w-0">
            <h2 id={idDoTitulo} className="text-lg font-semibold text-slate-900">
              {titulo}
            </h2>
            {descricao ? <div className="mt-1 text-sm leading-6 text-slate-500">{descricao}</div> : null}
          </div>
          <button
            type="button"
            onClick={aoFechar}
            aria-label="Fechar"
            className="-mr-2 -mt-1 inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-700"
          >
            <X size={18} />
          </button>
        </div>
        {children ? <div className="overflow-y-auto px-6 py-5">{children}</div> : null}
        {rodape ? <div className="flex flex-wrap justify-end gap-2 border-t border-slate-100 bg-slate-50/60 px-6 py-4">{rodape}</div> : null}
      </div>
    </div>
  );
}

export function Recado({ tom, children }: { tom: "erro" | "atencao" | "info"; children: ReactNode }) {
  const classes =
    tom === "erro"
      ? "border-rose-200 bg-rose-50 text-rose-700"
      : tom === "atencao"
        ? "border-amber-200 bg-amber-50 text-amber-900"
        : "border-slate-200 bg-white text-slate-600";
  return <div className={`rounded-xl border px-4 py-3 text-sm leading-6 ${classes}`}>{children}</div>;
}
