import { ErroDigital, type ContextoDigital } from "../contexto";
import { criarLinkDaEntrega } from "../entrega";
import { anexarArquivoFinal, conferirPedidoDigital, listarEncomendas, listarEntregas, reativarDoFreio, reenviarEntrega } from "../entregas";
import { acordarFilaDigital } from "../fila";
import { enderecoDaLoja, lerLojaDosDigitais } from "../loja";

/*
 * O que as rotas de Entregas fazem (app/api/digitais/entregas/**), fora
 * delas: a tela do painel e o assistente (MCP) chamam as mesmas funções.
 */

const ABERTOS = ["ativo", "aguardando_arquivo", "aguardando_codigo", "agendado"];

/* Os pedidos com produto digital: o acesso, o e-mail e o que precisa da loja
   (as encomendas esperando o arquivo final vêm à parte). */
export async function listarEntregasDaLoja({ admin, lojaId, pode }: ContextoDigital) {
  const [lista, encomendas] = await Promise.all([listarEntregas(admin, lojaId), listarEncomendas(admin, lojaId)]);
  return { podeEditar: pode("digital.deliveries.write"), ...lista, encomendas };
}

export type AcaoNaEntrega = "reenviar" | "link" | "liberar" | "anexar" | "reativar";

/*
 * As mãos da loja numa entrega:
 * - reenviar: o e-mail volta para a fila (pode ir para outro endereço);
 * - link: um link da entrega para mandar pelo WhatsApp;
 * - liberar: entrega agora, mesmo sem o pagamento registrado (a loja decide);
 * - anexar: o arquivo final de uma unidade sob encomenda (o comprador recebe);
 * - reativar: o acesso que o freio de compartilhamento pausou.
 */
export async function agirNaEntrega({ admin, lojaId, user }: ContextoDigital, pedidoId: string, bruto: unknown, request: Request) {
  const corpo = bruto as { acao?: unknown; email?: unknown; acessoId?: unknown; arquivoId?: unknown } | null;
  const acao = corpo?.acao;

  if (acao === "anexar") {
    const acessoId = typeof corpo?.acessoId === "string" ? corpo.acessoId : "";
    const arquivoId = typeof corpo?.arquivoId === "string" ? corpo.arquivoId : "";
    if (!/^[0-9a-f-]{36}$/i.test(acessoId)) throw new ErroDigital("Esta encomenda não foi encontrada.", 404);
    const { data: daEncomenda } = await admin
      .from("digital_entitlements")
      .select("id")
      .eq("id", acessoId)
      .eq("user_id", lojaId)
      .eq("order_id", pedidoId)
      .maybeSingle();
    if (!daEncomenda) throw new ErroDigital("Esta encomenda não foi encontrada.", 404);
    await anexarArquivoFinal(admin, { lojaId, acessoId, arquivoId, quem: user.id });
    acordarFilaDigital();
    return { ok: true };
  }

  if (acao === "reativar") {
    const reativados = await reativarDoFreio(admin, { lojaId, pedidoId });
    if (!reativados) throw new ErroDigital("Este pedido não tem acesso pausado pelo freio.", 409);
    /* O link antigo deixou de valer: um e-mail novo sai para o comprador. */
    const reenvio = await reenviarEntrega(admin, { lojaId, pedidoId, email: null }).catch(() => null);
    acordarFilaDigital();
    return { ok: true, reativados, destino: reenvio?.destino ?? null };
  }

  if (acao === "reenviar") {
    const email = typeof corpo?.email === "string" && corpo.email.trim() ? corpo.email.trim() : null;
    const resultado = await reenviarEntrega(admin, { lojaId, pedidoId, email });
    acordarFilaDigital();
    return { ok: true, ...resultado };
  }

  if (acao === "link") {
    const pedido = await conferirPedidoDigital(admin, lojaId, pedidoId);
    if (pedido.status === "canceled") throw new ErroDigital("O pedido foi cancelado: não há o que entregar.", 409);
    const { count } = await admin
      .from("digital_entitlements")
      .select("id", { count: "exact", head: true })
      .eq("order_id", pedidoId)
      .in("status", ABERTOS);
    if (!count) throw new ErroDigital("O acesso deste pedido ainda não foi liberado.", 409);
    const loja = await lerLojaDosDigitais(admin, lojaId);
    const base = enderecoDaLoja(request, loja);
    if (!base) throw new ErroDigital("Sua loja ainda não tem endereço público.", 409);
    const { data: compra } = await admin.from("catalog_orders").select("id").eq("approved_order_id", pedidoId).eq("user_id", lojaId).limit(1);
    const link = await criarLinkDaEntrega(admin, {
      lojaId,
      orderId: pedidoId,
      clienteId: pedido.client_id,
      catalogOrderId: ((compra ?? []) as Array<{ id: string }>)[0]?.id ?? null,
      base,
    });
    if (!link) throw new ErroDigital("Não foi possível criar o link agora.", 500);
    return { ok: true, link };
  }

  if (acao === "liberar") {
    const pedido = await conferirPedidoDigital(admin, lojaId, pedidoId);
    if (pedido.status === "canceled") throw new ErroDigital("O pedido foi cancelado. Reabra o pedido antes de liberar.", 409);
    if (pedido.clients?.is_system_placeholder) {
      throw new ErroDigital("Este pedido está no cliente genérico do catálogo. Escolha o cliente no pedido e libere de novo.", 409);
    }
    const { data, error } = await admin.rpc("liberar_entregas_digitais", { p_order_id: pedidoId, p_forcar: true, p_quem: user.id });
    if (error) throw new ErroDigital("Não foi possível liberar agora. Tente de novo.", 500);
    const resultado = (data ?? {}) as { ok?: boolean; motivo?: string; criados?: number; sem_entregavel?: number };
    if (!resultado.ok) {
      const motivos: Record<string, string> = {
        sem_cliente: "Este pedido está no cliente genérico do catálogo. Escolha o cliente no pedido e libere de novo.",
        cancelado: "O pedido foi cancelado.",
        sem_digital: "Este pedido não tem produto digital.",
      };
      throw new ErroDigital(motivos[String(resultado.motivo)] ?? "Não foi possível liberar.", 409);
    }
    /* A pendência deste pedido, se havia, está resolvida. */
    await admin
      .from("digital_tasks")
      .update({ situacao: "feito", atualizado_em: new Date().toISOString() })
      .eq("user_id", lojaId)
      .eq("tipo", "liberar")
      .eq("ref", pedidoId)
      .eq("situacao", "falhou");
    acordarFilaDigital();
    return { ok: true, criados: Number(resultado.criados ?? 0), semEntregavel: Number(resultado.sem_entregavel ?? 0) };
  }

  throw new ErroDigital("Ação inválida.", 400);
}
