"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  conferirItensNoBanco,
  esgotadoNaLista,
  faltasPorLinha,
  fraseDasFaltas,
  itensParaConferir,
  type Disponibilidade,
  type ItemParaConferir,
  type RespostaDaConferencia,
} from "@/lib/estoque-da-venda";

/*
  O estoque na hora de vender, no navegador do painel (PDV e orçamento).

  - useEstoqueV2Ligado: a empresa está no estoque v2?
  - useConferenciaDoEstoque: o carrinho/orçamento inteiro conferido no banco
    (estoque_conferir_itens, soma por célula), a cada mudança, e a frase de
    falta por linha. `conferirAgora` confere na hora (antes de cobrar).
  - useEsgotadosDaLista: quem está esgotado na lista de produtos (sem
    escolha, quantidade 1) — só no estoque v2.

  O cliente do Supabase é o da sessão: a função confere a permissão
  (products.read) e a empresa.
*/

type Rpc = {
  rpc: (fn: string, params?: Record<string, unknown>) => PromiseLike<{ data: unknown; error: { message?: string } | null }>;
};

const ligadas = new Map<string, boolean>();

export function useEstoqueV2Ligado(supabase: Rpc | null, dono: string | null | undefined): boolean | null {
  const id = String(dono || "");
  const [ligado, setLigado] = useState<boolean | null>(() => (id && ligadas.has(id) ? ligadas.get(id)! : null));
  useEffect(() => {
    if (!supabase || !id) return;
    if (ligadas.has(id)) {
      setLigado(ligadas.get(id)!);
      return;
    }
    let vivo = true;
    void Promise.resolve(supabase.rpc("estoque_v2_ligado", { p_empresa: id })).then(({ data, error }) => {
      if (!vivo) return;
      const v2 = !error && data === true;
      if (!error) ligadas.set(id, v2);
      setLigado(v2);
    });
    return () => {
      vivo = false;
    };
  }, [supabase, id]);
  return ligado;
}

export type ConferenciaDoEstoque = {
  /** Linha (índice da lista que veio) → frases de falta. */
  faltas: Map<number, string[]>;
  /** Todas as faltas numa frase (null = cabe). */
  frase: string | null;
  conferindo: boolean;
  /** Confere agora, sem esperar (antes de cobrar / aprovar). null = cabe ou não deu para conferir. */
  conferirAgora: () => Promise<string | null>;
};

export function useConferenciaDoEstoque(params: {
  supabase: Rpc | null;
  dono: string | null | undefined;
  linhas: ItemParaConferir[];
  /** false = não confere (ex.: empresa fora do v2 numa tela que só confere no v2). */
  ativo: boolean;
}): ConferenciaDoEstoque {
  const { supabase, dono, linhas, ativo } = params;
  const pedido = useMemo(() => itensParaConferir(linhas), [linhas]);
  const chave = useMemo(
    () => (ativo && dono && pedido.itens.length ? JSON.stringify({ dono, itens: pedido.itens }) : ""),
    [ativo, dono, pedido]
  );
  const [estado, setEstado] = useState<{ chave: string; faltas: Map<number, string[]> }>({ chave: "", faltas: new Map() });
  const [conferindo, setConferindo] = useState(false);
  const ultima = useRef("");

  const conferir = useCallback(
    async (alvo: string): Promise<Map<number, string[]> | null> => {
      if (!supabase || !alvo) return new Map();
      const corpo = JSON.parse(alvo) as { dono: string; itens: Parameters<typeof conferirItensNoBanco>[2] };
      const resposta = await conferirItensNoBanco(supabase, corpo.dono, corpo.itens).catch(() => null);
      if (!resposta) return null;
      const origem = itensParaConferir(linhas).origem;
      return faltasPorLinha(resposta as RespostaDaConferencia, origem);
    },
    [supabase, linhas]
  );

  useEffect(() => {
    if (!chave) {
      setEstado((antes) => (antes.faltas.size ? { chave: "", faltas: new Map() } : antes));
      return;
    }
    ultima.current = chave;
    setConferindo(true);
    const espera = window.setTimeout(async () => {
      const faltas = await conferir(chave);
      if (ultima.current !== chave) return;
      setConferindo(false);
      if (faltas) setEstado({ chave, faltas });
    }, 350);
    return () => window.clearTimeout(espera);
  }, [chave, conferir]);

  const conferirAgora = useCallback(async () => {
    if (!chave) return null;
    const faltas = await conferir(chave);
    if (!faltas) return null;
    setEstado({ chave, faltas });
    return fraseDasFaltas(faltas);
  }, [chave, conferir]);

  const faltas = estado.chave === chave ? estado.faltas : new Map<number, string[]>();
  return { faltas, frase: fraseDasFaltas(faltas), conferindo, conferirAgora };
}

/** Quem está esgotado na lista (só v2): id → true. Pergunta só pelos ids que vierem. */
export function useEsgotadosDaLista(params: { supabase: Rpc | null; dono: string | null | undefined; ids: string[]; ativo: boolean }) {
  const { supabase, dono, ids, ativo } = params;
  const [mapa, setMapa] = useState<Record<string, Disponibilidade>>({});
  const conhecidos = useRef<Record<string, Disponibilidade>>({});
  const [versao, setVersao] = useState(0);
  const faltam = useMemo(
    () => (ativo ? ids.filter((id) => !(id in conhecidos.current)) : []),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [ativo, ids, versao]
  );
  const chave = faltam.join(",");

  useEffect(() => {
    if (!supabase || !dono || !chave) return;
    let vivo = true;
    const lista = chave.split(",");
    void (async () => {
      const novos: Record<string, Disponibilidade> = {};
      const emLote = await supabase.rpc("estoque_disponivel_lista", { p_dono: dono, p_produtos: lista });
      if (!emLote.error && emLote.data && typeof emLote.data === "object") {
        Object.assign(novos, emLote.data as Record<string, Disponibilidade>);
      } else {
        /* Sem a função da lista (migration 212): um por um, poucos de cada vez. */
        for (let i = 0; i < lista.length; i += 6) {
          const lote = lista.slice(i, i + 6);
          const respostas = await Promise.all(
            lote.map((id) =>
              Promise.resolve(
                supabase.rpc("estoque_disponivel", {
                  p_produto: id,
                  p_selected_options: null,
                  p_quantidade: 1,
                  p_largura: null,
                  p_altura: null,
                  p_maximo: true,
                })
              )
            )
          );
          respostas.forEach((r, k) => {
            if (!r.error && r.data) novos[lote[k]] = r.data as Disponibilidade;
          });
        }
      }
      for (const id of lista) if (!(id in novos)) novos[id] = {};
      conhecidos.current = { ...conhecidos.current, ...novos };
      if (vivo) setMapa({ ...conhecidos.current });
    })();
    return () => {
      vivo = false;
    };
  }, [supabase, dono, chave]);

  /** Esquecer o que já sabe (depois de uma venda, o saldo mudou). */
  const esquecer = useCallback(() => {
    conhecidos.current = {};
    setMapa({});
    setVersao((v) => v + 1);
  }, []);

  const esgotado = useCallback((id: string) => esgotadoNaLista(mapa[id]), [mapa]);
  return { disponibilidade: mapa, esgotado, esquecer };
}
