import { avisarLoja } from "@/lib/loja/avisar-loja";
import { situacaoDoEstoque } from "@/lib/digitais/codigos";
import {
  conferirLinkDeAcesso,
  lerCamposDoComprador,
  lerConfigDigital,
  pendenciasParaPublicar,
  precoValido,
  ROTULO_DO_TIPO,
  tipoDigitalValido,
} from "@/lib/digitais/produto";

import { adicionarCodigos, estoqueDeCodigos, garantirEntregavelDeCodigos } from "../codigos";
import { ErroDigital, type ContextoDigital } from "../contexto";
import { atualizarResumoDoCurso, garantirEntregavelDoCurso, lerEstruturaDoCurso, pendenciasDoCurso } from "../cursos";
import { acordarFilaDigital } from "../fila";
import { enderecoDaLoja, lerLojaDosDigitais } from "../loja";
import {
  atualizarOQueRecebe,
  COLUNAS_DO_PRODUTO,
  lerEntregaveis,
  lerProdutoDaLoja,
  sincronizarEncomenda,
  vendasDoProduto,
  type ProdutoDigitalNoBanco,
} from "../produtos";

/*
 * O que as rotas de produtos digitais fazem (app/api/digitais/produtos/**),
 * fora delas: a tela do painel e o assistente (MCP) chamam as mesmas funções,
 * com as mesmas conferências. A rota só abre o contexto e lê o corpo.
 * `request` serve ao aviso da loja e ao endereço público.
 */

/* A lista de produtos digitais da loja, com os números de cada um. */
export async function listarProdutosDigitais({ admin, lojaId, pode }: ContextoDigital) {
  const [produtos, numeros] = await Promise.all([
    admin
      .from("products")
      .select(COLUNAS_DO_PRODUTO)
      .eq("user_id", lojaId)
      .eq("product_kind", "digital")
      .order("created_at", { ascending: false })
      .limit(500),
    admin.rpc("estatisticas_dos_produtos_digitais", { p_loja: lojaId }),
  ]);
  if (produtos.error) throw new ErroDigital("Não foi possível ler os produtos digitais.", 500);
  const lista = (produtos.data ?? []) as ProdutoDigitalNoBanco[];
  /* Código ou licença: quantos sobram e quem espera; grátis: quantos aceitaram novidades. */
  const [estoque, aceites] = await Promise.all([
    estoqueDeCodigos(admin, lojaId, lista),
    lista.some((p) => !(Number(p.price) > 0))
      ? admin
          .from("digital_consents")
          .select("product_id")
          .eq("user_id", lojaId)
          .eq("novidades", true)
          .in(
            "product_id",
            lista.filter((p) => !(Number(p.price) > 0)).map((p) => p.id)
          )
          .limit(10000)
      : Promise.resolve({ data: [] }),
  ]);
  /* Live: o produto aponta para a sala (o editor é o das Lives). */
  const lives = lista.some((p) => lerConfigDigital(p.digital_config).tipo === "live")
    ? await admin.from("live_salas").select("id,product_id").eq("user_id", lojaId).not("product_id", "is", null).limit(1000)
    : { data: [] };
  const salaDoProduto = new Map(((lives.data ?? []) as Array<{ id: string; product_id: string }>).map((l) => [l.product_id, l.id]));
  const novidadesPorProduto = new Map<string, number>();
  for (const linha of (aceites.data ?? []) as Array<{ product_id: string | null }>) {
    if (linha.product_id) novidadesPorProduto.set(linha.product_id, (novidadesPorProduto.get(linha.product_id) ?? 0) + 1);
  }
  const porProduto = new Map(
    ((numeros.data ?? []) as Array<{ product_id: string; pedidos_30: number; receita_30: number; com_acesso: number; downloads: number }>).map((linha) => [
      linha.product_id,
      linha,
    ])
  );
  return {
    podeEditar: pode("digital.products.write"),
    produtos: lista.map((produto) => {
      const config = lerConfigDigital(produto.digital_config);
      const n = porProduto.get(produto.id);
      const codigos = estoque.get(produto.id);
      return {
        id: produto.id,
        nome: produto.name,
        preco: Number(produto.price) || 0,
        imagem: produto.image_url,
        naLoja: Boolean(produto.show_in_catalog) && produto.active !== false,
        tipo: config.tipo,
        rotuloDoTipo: ROTULO_DO_TIPO[config.tipo],
        liveId: salaDoProduto.get(produto.id) ?? null,
        itens: config.recebe.length,
        criadoEm: produto.created_at,
        pedidos30: Number(n?.pedidos_30 ?? 0),
        receita30: Number(n?.receita_30 ?? 0),
        comAcesso: Number(n?.com_acesso ?? 0),
        downloads: Number(n?.downloads ?? 0),
        /* "Sobrando" são os que ainda dá para vender: os guardados para a pré-venda não contam. */
        codigos: codigos
          ? {
              livres: codigos.aVenda,
              total: codigos.total,
              esperando: codigos.esperando,
              situacao: situacaoDoEstoque(codigos.aVenda, codigos.avisarCom),
            }
          : null,
        aceitaramNovidades: novidadesPorProduto.get(produto.id) ?? 0,
        sobEncomenda: config.entrega.modo === "encomenda",
        preVenda: config.preVenda && new Date(config.preVenda.em).getTime() > Date.now() ? config.preVenda.em : null,
      };
    }),
  };
}

/* Cria o rascunho de um produto digital (fora da loja até a pessoa publicar). */
export async function criarProdutoDigital({ admin, lojaId }: ContextoDigital, bruto: unknown) {
  const corpo = bruto as { tipo?: unknown; nome?: unknown } | null;
  const tipo = tipoDigitalValido(corpo?.tipo);
  if (!tipo) throw new ErroDigital("Escolha o tipo do produto.", 400);
  /* A live nasce pela tela de Lives: ela cria o produto junto com a sala. */
  if (tipo === "live") throw new ErroDigital("Crie a live em Produtos digitais → Lives.", 400);
  const nome =
    typeof corpo?.nome === "string" && corpo.nome.trim()
      ? corpo.nome.trim().slice(0, 200)
      : tipo === "curso"
        ? "Novo conteúdo em vídeo"
        : `Novo produto: ${ROTULO_DO_TIPO[tipo].toLowerCase()}`;
  const { data, error } = await admin
    .from("products")
    .insert({
      name: nome,
      price: 0,
      user_id: lojaId,
      product_kind: "digital",
      digital_config: { ...lerConfigDigital({ tipo }), tipo },
      show_in_catalog: false,
      active: true,
    })
    .select("id")
    .single();
  if (error || !data) throw new ErroDigital("Não foi possível criar o produto.", 500);
  const id = (data as { id: string }).id;
  if (tipo === "curso") {
    /* O curso nasce com o acesso (entregável "curso") e o primeiro módulo. */
    await garantirEntregavelDoCurso(admin, lojaId, id);
    await admin.from("digital_course_modules").insert({ user_id: lojaId, product_id: id, titulo: "Boas-vindas", posicao: 0 });
    await atualizarResumoDoCurso(admin, lojaId, id).catch(() => undefined);
  }
  /* Código ou licença nasce com o item que leva o código de cada compra. */
  if (tipo === "codigo") await garantirEntregavelDeCodigos(admin, lojaId, id);
  return { ok: true, id };
}

/* Um produto digital inteiro: a ficha, o que o comprador recebe e os números. */
export async function lerProdutoDigital({ admin, lojaId, pode }: ContextoDigital, id: string, request: Request) {
  const [produto, entregaveis, vendas, loja, numeros, campos] = await Promise.all([
    lerProdutoDaLoja(admin, lojaId, id),
    lerEntregaveis(admin, lojaId, id),
    vendasDoProduto(admin, id),
    lerLojaDosDigitais(admin, lojaId),
    admin.rpc("estatisticas_dos_produtos_digitais", { p_loja: lojaId }),
    admin.from("products").select("custom_fields_config").eq("id", id).eq("user_id", lojaId).maybeSingle(),
  ]);
  const config = lerConfigDigital(produto.digital_config);
  const n = ((numeros.data ?? []) as Array<{ product_id: string; pedidos_30: number; receita_30: number; com_acesso: number; downloads: number }>).find(
    (linha) => linha.product_id === id
  );
  const base = enderecoDaLoja(request, loja);
  return {
    podeEditar: pode("digital.products.write"),
    produto: {
      id: produto.id,
      nome: produto.name,
      descricao: produto.description ?? "",
      preco: Number(produto.price) || 0,
      imagem: produto.image_url,
      naLoja: Boolean(produto.show_in_catalog) && produto.active !== false,
      tipo: config.tipo,
      rotuloDoTipo: ROTULO_DO_TIPO[config.tipo],
      config,
      vendas,
      enderecoNaLoja: base ? `${base}/p/${produto.id}` : null,
      /* Sob encomenda: o que o comprador preenche na compra. */
      campos: lerCamposDoComprador((campos.data as { custom_fields_config?: unknown } | null)?.custom_fields_config),
    },
    entregaveis,
    numeros: {
      pedidos30: Number(n?.pedidos_30 ?? 0),
      receita30: Number(n?.receita_30 ?? 0),
      comAcesso: Number(n?.com_acesso ?? 0),
      downloads: Number(n?.downloads ?? 0),
    },
  };
}

export type EdicaoDoProdutoDigital = {
  nome?: unknown;
  descricao?: unknown;
  preco?: unknown;
  imagem?: unknown;
  naLoja?: unknown;
  config?: unknown;
  campos?: unknown;
};

function imagemValida(valor: unknown): string | null {
  if (typeof valor !== "string" || !valor.trim()) return null;
  try {
    const url = new URL(valor.trim());
    return url.protocol === "https:" ? url.toString() : null;
  } catch {
    return null;
  }
}

/* Salva a ficha. Publicar na loja confere antes se não falta nada. */
export async function salvarProdutoDigital({ admin, lojaId, situacao }: ContextoDigital, id: string, bruto: unknown, request: Request) {
  const produto = await lerProdutoDaLoja(admin, lojaId, id);
  const corpo = bruto as EdicaoDoProdutoDigital | null;
  if (!corpo || typeof corpo !== "object") throw new ErroDigital("Não recebemos as mudanças.", 400);

  const atual = lerConfigDigital(produto.digital_config);
  const mudancas: Record<string, unknown> = {};

  if ("nome" in corpo) {
    const nome = typeof corpo.nome === "string" ? corpo.nome.trim().slice(0, 200) : "";
    if (!nome) throw new ErroDigital("Dê um nome ao produto.", 400);
    mudancas.name = nome;
  }
  if ("descricao" in corpo) {
    mudancas.description = typeof corpo.descricao === "string" ? corpo.descricao.replace(/\u0000/g, "").slice(0, 20000) : null;
  }
  if ("preco" in corpo) {
    const preco = precoValido(corpo.preco);
    if (preco === null) throw new ErroDigital("Confira o preço.", 400);
    mudancas.price = preco;
  }
  if ("imagem" in corpo) {
    mudancas.image_url = imagemValida(corpo.imagem);
  }
  if ("config" in corpo) {
    /* O tipo e o "o que você recebe" não mudam por aqui: o primeiro é fixo
       depois de criado e o segundo sai dos entregáveis. No curso, o resumo
       do conteúdo sai dos módulos e aulas, nunca da tela. */
    const nova = lerConfigDigital(corpo.config, atual.tipo);
    /* Sob encomenda só para arquivos e link; pré-venda só com data no futuro
       (a mesma data que já passou desliga sozinha). */
    const entrega =
      atual.tipo === "arquivos" || atual.tipo === "link" ? nova.entrega : { modo: "na_hora" as const, prazoDias: nova.entrega.prazoDias };
    const preVenda = nova.preVenda && new Date(nova.preVenda.em).getTime() > Date.now() ? nova.preVenda : null;
    if (nova.preVenda && !preVenda && nova.preVenda.em !== atual.preVenda?.em) {
      throw new ErroDigital("A data da pré-venda precisa ser depois de hoje.", 400);
    }
    mudancas.digital_config = {
      ...nova,
      tipo: atual.tipo,
      recebe: atual.recebe,
      entrega,
      preVenda,
      ...(atual.tipo === "curso" && nova.curso && atual.curso ? { curso: { ...nova.curso, conteudo: atual.curso.conteudo } } : {}),
    };
  }
  if ("campos" in corpo) {
    mudancas.custom_fields_config = lerCamposDoComprador(corpo.campos);
  }

  let publicar: boolean | null = null;
  if ("naLoja" in corpo) publicar = corpo.naLoja === true;

  /* Grátis é para quem assina: no teste, o produto que já está na loja não vira grátis. */
  if (situacao === "teste" && "price" in mudancas && Number(mudancas.price) <= 0 && produto.show_in_catalog && publicar !== false) {
    throw new ErroDigital("Produto grátis é para quem assina o Ktalogu. No período de teste, coloque um preço.", 400);
  }

  /* O item "arquivo feito para você" acompanha o jeito de entregar. */
  const entregaNova = (mudancas.digital_config as { entrega?: { modo: "na_hora" | "encomenda" } } | undefined)?.entrega;
  if (entregaNova && (atual.tipo === "arquivos" || atual.tipo === "link") && entregaNova.modo !== atual.entrega.modo) {
    await sincronizarEncomenda(admin, lojaId, id, entregaNova.modo);
  }

  if (publicar === true) {
    const entregaveis = await lerEntregaveis(admin, lojaId, id);
    const ativos = entregaveis.filter((e) => e.ativo);
    let codigosLivres: number | null = null;
    if (atual.tipo === "codigo") {
      const entregavelId = await garantirEntregavelDeCodigos(admin, lojaId, id);
      const { count } = await admin
        .from("digital_codes")
        .select("id", { count: "exact", head: true })
        .eq("deliverable_id", entregavelId)
        .is("entitlement_id", null)
        .is("apagado_em", null);
      codigosLivres = count ?? 0;
    }
    const precoFinal = Number(mudancas.price ?? produto.price ?? 0);
    const faltas =
      atual.tipo === "curso"
        ? pendenciasDoCurso({
            nome: String(mudancas.name ?? produto.name ?? ""),
            imagem: (mudancas.image_url as string | null | undefined) ?? produto.image_url,
            modulos: await lerEstruturaDoCurso(admin, lojaId, id),
          })
        : pendenciasParaPublicar({
            nome: String(mudancas.name ?? produto.name ?? ""),
            preco: Number(mudancas.price ?? produto.price ?? 0),
            imagem: (mudancas.image_url as string | null | undefined) ?? produto.image_url,
            entregaveisAtivos: ativos.length,
            arquivosNaoProntos: ativos.filter((e) => e.tipo === "arquivo" && e.arquivo?.status !== "pronto").length,
            codigosLivres,
            /* Grátis é para quem assina (no período de teste, coloque um preço). */
            gratisSemAssinatura: precoFinal <= 0 && situacao === "teste",
          });
    if (faltas.length) throw new ErroDigital("Ainda falta um pouco para ir para a loja.", 400, { faltas });
    mudancas.show_in_catalog = true;
    mudancas.active = true;
  } else if (publicar === false) {
    mudancas.show_in_catalog = false;
  }

  if (Object.keys(mudancas).length === 0) return { ok: true };

  const { error } = await admin
    .from("products")
    .update(mudancas)
    .eq("id", id)
    .eq("user_id", lojaId)
    .eq("product_kind", "digital");
  if (error) throw new ErroDigital("Não foi possível salvar o produto.", 500);

  /* A vitrine guarda a loja por 2 minutos: avisa para mostrar já. */
  if (produto.show_in_catalog || publicar !== null) avisarLoja(lojaId, request);
  return { ok: true };
}

/* Apaga o rascunho. Produto que já vendeu não some: sai da loja. */
export async function apagarProdutoDigital({ admin, lojaId }: ContextoDigital, id: string, request: Request) {
  const produto = await lerProdutoDaLoja(admin, lojaId, id);
  const [vendas, acessos] = await Promise.all([
    vendasDoProduto(admin, id),
    admin.from("digital_entitlements").select("id", { count: "exact", head: true }).eq("product_id", id),
  ]);
  if (vendas > 0 || (acessos.count ?? 0) > 0) {
    throw new ErroDigital("Este produto já tem quem comprou ou recebeu acesso, e ele continua valendo. Tire da loja em vez de apagar.", 409, { vendas });
  }
  const emUso = "Este produto aparece em pedidos da loja. Tire da loja em vez de apagar.";
  const { error: erroDosEntregaveis } = await admin.from("digital_deliverables").delete().eq("product_id", id).eq("user_id", lojaId);
  if (erroDosEntregaveis) throw new ErroDigital(erroDosEntregaveis.code === "23503" ? emUso : "Não foi possível apagar o produto.", erroDosEntregaveis.code === "23503" ? 409 : 500);
  const { error } = await admin.from("products").delete().eq("id", id).eq("user_id", lojaId).eq("product_kind", "digital");
  if (error) throw new ErroDigital(error.code === "23503" ? emUso : "Não foi possível apagar o produto.", error.code === "23503" ? 409 : 500);
  if (produto.show_in_catalog) avisarLoja(lojaId, request);
  return { ok: true };
}

/* --------------------------------------------- o que o comprador recebe */

type NovoEntregavel = { tipo?: unknown; titulo?: unknown; descricao?: unknown; fileId?: unknown; url?: unknown; instrucoes?: unknown };

const texto = (valor: unknown, teto: number) => (typeof valor === "string" && valor.trim() ? valor.replace(/\u0000/g, "").trim().slice(0, teto) : null);

/* Acrescenta o que o comprador recebe: um arquivo da biblioteca, um link ou um texto. */
export async function adicionarEntregavel({ admin, lojaId }: ContextoDigital, id: string, bruto: unknown, request: Request) {
  const produto = await lerProdutoDaLoja(admin, lojaId, id);
  const corpo = bruto as NovoEntregavel | null;
  const tipo = corpo?.tipo;
  if (tipo !== "arquivo" && tipo !== "link" && tipo !== "texto") throw new ErroDigital("Escolha o que o comprador recebe.", 400);

  const titulo = texto(corpo?.titulo, 160);
  const descricao = texto(corpo?.descricao, 1000);
  const linha: Record<string, unknown> = { user_id: lojaId, product_id: id, tipo, descricao };

  if (tipo === "arquivo") {
    const fileId = typeof corpo?.fileId === "string" ? corpo.fileId : "";
    if (!/^[0-9a-f-]{36}$/i.test(fileId)) throw new ErroDigital("Escolha o arquivo.", 400);
    const { data: arquivo } = await admin
      .from("digital_files")
      .select("id,nome,status")
      .eq("id", fileId)
      .eq("user_id", lojaId)
      .maybeSingle();
    const a = arquivo as { id: string; nome: string; status: string } | null;
    if (!a || a.status === "apagado" || a.status === "recusado") throw new ErroDigital("Esse arquivo não está mais na sua biblioteca.", 400);
    linha.file_id = a.id;
    linha.titulo = titulo ?? a.nome.replace(/\.[a-z0-9]{1,10}$/i, "").slice(0, 160);
  } else if (tipo === "link") {
    const link = conferirLinkDeAcesso(corpo?.url);
    if (!link.ok) throw new ErroDigital(link.motivo, 400);
    linha.url = link.url;
    linha.instrucoes = texto(corpo?.instrucoes, 4000);
    linha.titulo = titulo ?? "Link de acesso";
  } else {
    const instrucoes = texto(corpo?.instrucoes, 4000);
    if (!instrucoes) throw new ErroDigital("Escreva o texto que o comprador recebe.", 400);
    linha.instrucoes = instrucoes;
    linha.titulo = titulo ?? "Instruções";
  }

  const atuais = await lerEntregaveis(admin, lojaId, id);
  if (atuais.length >= 20) throw new ErroDigital("Cada produto entrega até 20 itens.", 400);
  linha.posicao = atuais.reduce((maior, e) => Math.max(maior, e.posicao), -1) + 1;

  const { data, error } = await admin.from("digital_deliverables").insert(linha).select("id").single();
  if (error || !data) throw new ErroDigital("Não foi possível guardar.", 500);
  await atualizarOQueRecebe(admin, lojaId, id);
  if (produto.show_in_catalog) avisarLoja(lojaId, request);
  return { ok: true, id: (data as { id: string }).id };
}

async function entregavelDoProduto(admin: ContextoDigital["admin"], lojaId: string, produtoId: string, entregavelId: string) {
  const lista = await lerEntregaveis(admin, lojaId, produtoId);
  const achado = lista.find((e) => e.id === entregavelId);
  if (!achado) throw new ErroDigital("Esse item não está mais no produto.", 404);
  return { achado, lista };
}

/* Muda um item: título, descrição, link, texto, ligado/desligado ou a ordem. */
export async function alterarEntregavel({ admin, lojaId }: ContextoDigital, id: string, entregavelId: string, bruto: unknown, request: Request) {
  const produto = await lerProdutoDaLoja(admin, lojaId, id);
  const { achado, lista } = await entregavelDoProduto(admin, lojaId, id, entregavelId);
  const corpo = bruto as Record<string, unknown> | null;
  if (!corpo) throw new ErroDigital("Não recebemos as mudanças.", 400);

  const mudancas: Record<string, unknown> = { atualizado_em: new Date().toISOString() };
  if ("titulo" in corpo) {
    const titulo = texto(corpo.titulo, 160);
    if (!titulo) throw new ErroDigital("Dê um nome ao item.", 400);
    mudancas.titulo = titulo;
  }
  if ("descricao" in corpo) mudancas.descricao = texto(corpo.descricao, 1000);
  if ("url" in corpo && achado.tipo === "link") {
    const link = conferirLinkDeAcesso(corpo.url);
    if (!link.ok) throw new ErroDigital(link.motivo, 400);
    mudancas.url = link.url;
  }
  if ("instrucoes" in corpo && (achado.tipo === "link" || achado.tipo === "texto")) {
    const instrucoes = texto(corpo.instrucoes, 4000);
    if (achado.tipo === "texto" && !instrucoes) throw new ErroDigital("Escreva o texto que o comprador recebe.", 400);
    mudancas.instrucoes = instrucoes;
  }
  if ("ativo" in corpo) mudancas.ativo = corpo.ativo === true;

  /* Subir ou descer na lista: troca de lugar com o vizinho e renumera. */
  if (corpo.mover === "cima" || corpo.mover === "baixo") {
    const ordem = [...lista].sort((a, b) => a.posicao - b.posicao);
    const indice = ordem.findIndex((e) => e.id === entregavelId);
    const alvo = corpo.mover === "cima" ? indice - 1 : indice + 1;
    if (indice >= 0 && alvo >= 0 && alvo < ordem.length) {
      [ordem[indice], ordem[alvo]] = [ordem[alvo], ordem[indice]];
      for (const [posicao, item] of ordem.entries()) {
        if (item.posicao === posicao) continue;
        const { error } = await admin
          .from("digital_deliverables")
          .update({ posicao, atualizado_em: new Date().toISOString() })
          .eq("id", item.id)
          .eq("user_id", lojaId);
        if (error) throw new ErroDigital("Não foi possível mudar a ordem.", 500);
      }
    }
  }

  const { error } = await admin.from("digital_deliverables").update(mudancas).eq("id", entregavelId).eq("user_id", lojaId).eq("product_id", id);
  if (error) throw new ErroDigital("Não foi possível salvar o item.", 500);
  await atualizarOQueRecebe(admin, lojaId, id);
  if (produto.show_in_catalog) avisarLoja(lojaId, request);
  return { ok: true };
}

/* Tira o item do produto. Quem já comprou continua recebendo (o item só some
   para as compras novas); sem compra nenhuma, ele sai de vez. */
export async function tirarEntregavel({ admin, lojaId }: ContextoDigital, id: string, entregavelId: string, request: Request) {
  const produto = await lerProdutoDaLoja(admin, lojaId, id);
  const achado = await entregavelDoProduto(admin, lojaId, id, entregavelId);
  /* O código, a encomenda e o curso são o jeito de entregar o produto: não
     saem por aqui (apagar o item dos códigos levaria a lista junto). */
  if (["codigos", "encomenda", "curso"].includes(String((achado as { tipo?: string }).tipo))) {
    throw new ErroDigital("Este item faz parte do jeito de entregar o produto. Para mudar, use \"Como entrega\".", 409);
  }
  const { count } = await admin
    .from("digital_entitlements")
    .select("id", { count: "exact", head: true })
    .eq("deliverable_id", entregavelId);
  if ((count ?? 0) > 0) {
    const { error } = await admin
      .from("digital_deliverables")
      .update({ ativo: false, apagado_em: new Date().toISOString(), atualizado_em: new Date().toISOString() })
      .eq("id", entregavelId)
      .eq("user_id", lojaId);
    if (error) throw new ErroDigital("Não foi possível tirar o item.", 500);
  } else {
    const { error } = await admin.from("digital_deliverables").delete().eq("id", entregavelId).eq("user_id", lojaId);
    if (error) throw new ErroDigital("Não foi possível tirar o item.", 500);
  }
  await atualizarOQueRecebe(admin, lojaId, id);
  if (produto.show_in_catalog) avisarLoja(lojaId, request);
  return { ok: true, compradoresMantidos: count ?? 0 };
}

/* --------------------------------------------- códigos e licenças */

/* Acrescenta códigos (colados ou importados); quem estava esperando já recebe. */
export async function adicionarCodigosAoProduto({ admin, lojaId }: ContextoDigital, id: string, bruto: unknown, request: Request) {
  const corpo = bruto as { texto?: unknown } | null;
  const resultado = await adicionarCodigos(admin, { lojaId, produtoId: id, texto: corpo?.texto });
  if (resultado.entregues > 0) acordarFilaDigital();
  /* Produto que estava esgotado volta a vender: a vitrine fica sabendo já. */
  const produto = await lerProdutoDaLoja(admin, lojaId, id);
  if (produto.show_in_catalog) avisarLoja(lojaId, request);
  return { ok: true, ...resultado };
}
