import type { SupabaseClient } from "@supabase/supabase-js";
import { marcarProduto, type EstoqueDaVitrine, type RespostaDoEstoque } from "@/lib/estoque-v2-da-loja";

/*
  Estoque v2 na loja — o lado do servidor (chave de serviço).

  - empresaNoEstoqueV2: a loja está no estoque v2? Lembrado por 60 s (a
    conversão é rara e a página da loja não pode ir ao banco a mais por isso).
  - comEstoqueV2: marca os produtos da vitrine com a conta do banco
    (estoque_disponivel, sem escolha, quantidade 1). Fora do v2 devolve a
    lista intacta: a loja segue com a regra de sempre.
  - estoqueDaEscolha: a conta do banco para a escolha exata do cliente.
*/

type Admin = Pick<SupabaseClient, "rpc" | "from">;

const LEMBRAR_MS = 60_000;
const memoria = globalThis as unknown as { __estoqueV2DaLoja?: Map<string, { v2: boolean; ate: number }> };
const ligadas = (memoria.__estoqueV2DaLoja ??= new Map());

export async function empresaNoEstoqueV2(admin: Admin, dono: string | null | undefined): Promise<boolean> {
  const id = String(dono || "").trim();
  if (!id) return false;
  const guardada = ligadas.get(id);
  if (guardada && guardada.ate > Date.now()) return guardada.v2;
  const { data, error } = await admin.rpc("estoque_v2_ligado", { p_empresa: id });
  /* Erro ao perguntar (banco fora, função ausente) = regra antiga, que é o
     que a loja sempre fez; não guarda para tentar de novo na próxima. */
  if (error) return false;
  const v2 = data === true;
  ligadas.set(id, { v2, ate: Date.now() + LEMBRAR_MS });
  return v2;
}

const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

/**
 * O resumo do banco para vários produtos. Usa estoque_disponivel_lista
 * (migration 212); sem ela, pergunta produto a produto, poucos de cada vez.
 */
async function resumosDaVitrine(admin: Admin, dono: string, ids: string[]): Promise<Map<string, EstoqueDaVitrine>> {
  const mapa = new Map<string, EstoqueDaVitrine>();
  if (!ids.length) return mapa;
  const lista = await admin.rpc("estoque_disponivel_lista", { p_dono: dono, p_produtos: ids });
  if (!lista.error && lista.data && typeof lista.data === "object") {
    for (const [id, resumo] of Object.entries(lista.data as Record<string, EstoqueDaVitrine>)) mapa.set(id, resumo);
    return mapa;
  }
  /* Sem a função da lista: só pergunta por quem pode limitar. Produto
     "nenhum" nunca limita (e sem escolha não leva serviço nenhum). */
  const { data: modos } = await admin.from("products").select("id,estoque_modo").in("id", ids);
  const perguntar = ((modos || []) as Array<{ id: string; estoque_modo: string | null }>)
    .filter((linha) => linha.estoque_modo !== "nenhum")
    .map((linha) => String(linha.id));
  const DE_CADA_VEZ = 8;
  for (let i = 0; i < perguntar.length; i += DE_CADA_VEZ) {
    const lote = perguntar.slice(i, i + DE_CADA_VEZ);
    const respostas = await Promise.all(
      lote.map((id) =>
        admin.rpc("estoque_disponivel", {
          p_produto: id,
          p_selected_options: null,
          p_quantidade: 1,
          p_largura: null,
          p_altura: null,
          p_maximo: false,
        })
      )
    );
    respostas.forEach((resposta, indice) => {
      if (resposta.error || !resposta.data) return;
      const d = resposta.data as RespostaDoEstoque;
      mapa.set(lote[indice], { modo: d.modo, controla: d.controla, limita: d.limita, cabe: d.cabe, motivo: d.motivo });
    });
  }
  return mapa;
}

/**
 * Os produtos da vitrine com as marcas do estoque v2 (estoque_v2,
 * estoque_v2_limita, estoque_v2_esgotado). Fora do v2: a mesma lista.
 */
export async function comEstoqueV2<T>(admin: Admin, dono: string | null | undefined, produtos: T[]): Promise<T[]> {
  if (!Array.isArray(produtos) || !produtos.length) return produtos;
  if (!(await empresaNoEstoqueV2(admin, dono))) return produtos;
  const ids = Array.from(
    new Set(
      produtos
        .map((produto) => String((produto as { id?: unknown } | null)?.id || ""))
        .filter((id) => UUID.test(id))
    )
  );
  let mapa = new Map<string, EstoqueDaVitrine>();
  try {
    mapa = await resumosDaVitrine(admin, String(dono), ids);
  } catch (erro) {
    console.error("[loja] estoque v2 da vitrine:", erro instanceof Error ? erro.message : erro);
  }
  return produtos.map((produto) =>
    produto && typeof produto === "object"
      ? (marcarProduto(produto as Record<string, unknown>, mapa.get(String((produto as { id?: unknown }).id))) as T)
      : produto
  );
}

/** A conta do banco para a escolha exata (página do produto, carrinho). */
export async function estoqueDaEscolhaNoBanco(
  admin: Admin,
  params: { produtoId: string; selected: unknown; quantidade: number; largura?: number | null; altura?: number | null }
): Promise<RespostaDoEstoque | null> {
  const { data, error } = await admin.rpc("estoque_disponivel", {
    p_produto: params.produtoId,
    p_selected_options: params.selected && typeof params.selected === "object" && !Array.isArray(params.selected) ? params.selected : null,
    p_quantidade: Math.max(Number(params.quantidade) || 1, 0),
    p_largura: Number(params.largura) > 0 ? Number(params.largura) : null,
    p_altura: Number(params.altura) > 0 ? Number(params.altura) : null,
    p_maximo: true,
  });
  if (error || !data) return null;
  return data as RespostaDoEstoque;
}
