import { NextResponse } from "next/server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import {
  chamarMercadoPago,
  credenciaisAtivas,
  dadosDoQr,
  estadoDoPagamento,
  lerConfiguracaoDoMercadoPago,
  vencimentoParaOProvedor,
} from "@/lib/mercado-pago";
import {
  chamarAsaas,
  clienteDoAsaas,
  credenciaisAtivas as credenciaisDoAsaas,
  estadoDoPagamento as estadoDoAsaas,
  instanteDoPagamento,
  lerConfiguracaoDoAsaas,
  qrDoPix,
  vencimentoNoBrasil,
} from "@/lib/asaas";
import { provedorDoLocal } from "@/lib/provedor-de-pagamento";

/*
  A cobrança Pix do balcão.

  O caixa fecha a conta, o sistema pede um QR ao Mercado Pago e mostra na tela
  virada para o cliente. Ele lê, paga, e o provedor confirma. Só então a venda
  é gravada — pelo checkout de sempre, com o pagamento apontando para esta
  cobrança.

  Três verbos:
  - POST   gera a cobrança (dez minutos de validade);
  - GET    pergunta como está — e, se ainda estiver pendente, confere no
           provedor, porque o aviso dele pode não chegar (máquina sem endereço
           público, aviso perdido, atraso);
  - DELETE cancela, para o cliente que desistiu ou vai pagar de outro jeito.
*/

export const runtime = "nodejs";

/* Quanto tempo o QR vale. Dez minutos é o bastante para abrir o app do banco
   e ler; mais do que isso é uma cobrança esquecida na tela. */
const VALIDADE_MINUTOS = 10;

/* Uma conferência no provedor a cada 3,5 s no máximo: a tela pergunta de 4 em
   4 s, e assim cada pergunta vira no máximo uma chamada. */
const INTERVALO_DE_CONFERENCIA_MS = 3500;

type LinhaDaCobranca = {
  id: string;
  company_id: string;
  tenant_id: string;
  integration_id: string | null;
  status: string;
  amount: number;
  description: string | null;
  provider_payment_id: string | null;
  provider_status: string | null;
  provider_status_detail: string | null;
  qr_code: string | null;
  qr_code_base64: string | null;
  ticket_url: string | null;
  expires_at: string | null;
  approved_at: string | null;
  checked_at: string | null;
  order_id: string | null;
  environment: string | null;
  snapshot: Record<string, unknown> | null;
  created_by: string;
};

/* A foto da venda guardada na cobrança: o suficiente para refazer a venda
   depois, sem depender de o cupom ainda estar na tela. */
function lerFoto(bruto: unknown) {
  if (!bruto || typeof bruto !== "object" || Array.isArray(bruto)) return null;
  const foto = bruto as Record<string, unknown>;
  const payload = foto.payload && typeof foto.payload === "object" && !Array.isArray(foto.payload) ? (foto.payload as Record<string, unknown>) : null;
  if (!payload || !Array.isArray(payload.items) || !payload.items.length) return null;
  return {
    payload,
    resumo: foto.resumo && typeof foto.resumo === "object" ? (foto.resumo as Record<string, unknown>) : null,
  };
}

type RespostaDoProvedor = Record<string, unknown> & {
  id?: unknown;
  status?: unknown;
  status_detail?: unknown;
  date_of_expiration?: unknown;
  date_approved?: unknown;
};

type CobrancaDoAsaas = Record<string, unknown> & {
  id?: unknown;
  status?: unknown;
  confirmedDate?: unknown;
  clientPaymentDate?: unknown;
  invoiceUrl?: unknown;
};

/* O que o balcao precisa saber da cobranca, venha do provedor que vier. */
type EstadoRemoto = {
  estado: string;
  providerStatus: string | null;
  detalhe: string;
  aprovadoEm: string | null;
  bruto: Record<string, unknown>;
};

function texto(valor: unknown) {
  return String(valor ?? "").trim();
}

function isUuid(valor: string) {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(valor);
}

function emailValido(valor: string) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(valor);
}

/* O que a tela precisa saber — nunca a resposta crua do provedor. */
function montarCobranca(linha: LinhaDaCobranca) {
  const vencida =
    linha.status === "pending" && linha.expires_at ? new Date(linha.expires_at).getTime() < Date.now() : false;
  return {
    id: linha.id,
    status: vencida ? "expired" : linha.status,
    valor: Number(linha.amount || 0),
    qrCode: linha.qr_code,
    qrCodeBase64: linha.qr_code_base64,
    ticketUrl: linha.ticket_url,
    expiraEm: linha.expires_at,
    aprovadaEm: linha.approved_at,
    pedidoId: linha.order_id,
    ambiente: linha.environment,
    detalhe: linha.provider_status_detail,
    foto: lerFoto(linha.snapshot),
  };
}

async function acesso() {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return { erro: NextResponse.json({ error: "Não autenticado." }, { status: 401 }) } as const;

  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "orders.write") || !canCompany(ctx, user.id, "financial.write")) {
    return { erro: NextResponse.json({ error: "Sem permissão para receber pelo PDV." }, { status: 403 }) } as const;
  }
  return { supabase, user, ctx } as const;
}

/*
  Quem cobra o Pix desta conta.

  O balcao nao pergunta se o checkout do catalogo esta ligado: quem vende so
  presencial mantem a loja online fechada e mesmo assim precisa do QR no caixa.

  O que volta e uma uniao discriminada por `nome`: cada ramo carrega a
  configuracao e a credencial do SEU provedor, e o TypeScript nao deixa
  misturar chave de um com chamada do outro.
*/
async function credenciaisDaLoja(ownerId: string) {
  const admin = createAdminClient();
  const escolhido = await provedorDoLocal(admin as never, ownerId, "balcao", "pix");
  if (!escolhido) throw new Error("Nenhum provedor de pagamento está configurado para o Pix do balcão.");

  if (escolhido.provedor === "asaas") {
    const config = lerConfiguracaoDoAsaas(escolhido.integracao.settings);
    const credenciais = credenciaisDoAsaas(config);
    if (!credenciais) throw new Error("As chaves do Asaas estão incompletas.");
    return { admin, integracao: escolhido.integracao, nome: "asaas" as const, config, credenciais };
  }

  const config = lerConfiguracaoDoMercadoPago(escolhido.integracao.settings);
  const credenciais = credenciaisAtivas(config);
  if (!credenciais) throw new Error("As chaves do Mercado Pago estão incompletas.");
  return { admin, integracao: escolhido.integracao, nome: "mercado_pago" as const, config, credenciais };
}

/*
  Como esta a cobranca no provedor.

  Usada pela conferencia ativa do balcao, que roda a cada quatro segundos
  enquanto o cliente nao paga. Os dois provedores respondem coisas diferentes e
  saem daqui iguais.
*/
async function lerNoProvedor(
  loja: Awaited<ReturnType<typeof credenciaisDaLoja>>,
  providerPaymentId: string
): Promise<EstadoRemoto> {
  if (loja.nome === "asaas") {
    const remoto = await chamarAsaas<CobrancaDoAsaas>(
      `/payments/${encodeURIComponent(providerPaymentId)}`,
      loja.credenciais,
      { method: "GET" }
    );
    return {
      estado: estadoDoAsaas(remoto.status),
      providerStatus: texto(remoto.status) || null,
      /* O Asaas nao tem detalhe separado; o motivo ja vem no proprio estado. */
      detalhe: "",
      aprovadoEm: instanteDoPagamento(remoto.confirmedDate) || instanteDoPagamento(remoto.clientPaymentDate) || null,
      bruto: remoto,
    };
  }

  const remoto = await chamarMercadoPago<RespostaDoProvedor>(
    `/v1/payments/${encodeURIComponent(providerPaymentId)}`,
    loja.credenciais.accessToken,
    { method: "GET" }
  );
  return {
    estado: estadoDoPagamento(remoto.status),
    providerStatus: texto(remoto.status) || null,
    detalhe: texto(remoto.status_detail),
    aprovadoEm: texto(remoto.date_approved) || null,
    bruto: remoto,
  };
}

/* Cancelar a cobranca no provedor. No Asaas a cobranca e apagada; no Mercado
   Pago o pagamento vira "cancelled". */
async function cancelarNoProvedor(loja: Awaited<ReturnType<typeof credenciaisDaLoja>>, providerPaymentId: string) {
  if (loja.nome === "asaas") {
    await chamarAsaas(`/payments/${encodeURIComponent(providerPaymentId)}`, loja.credenciais, { method: "DELETE" });
    return;
  }
  await chamarMercadoPago(`/v1/payments/${encodeURIComponent(providerPaymentId)}`, loja.credenciais.accessToken, {
    method: "PUT",
    body: JSON.stringify({ status: "cancelled" }),
  });
}

/* O endereço do aviso do provedor. O webhook mora no app da loja; sem endereço
   público (máquina local) não adianta mandar, e a conferência ativa resolve. */
function enderecoDoAviso() {
  const base = texto(process.env.NEXT_PUBLIC_PUBLIC_CATALOG_BASE_URL || process.env.PUBLIC_CATALOG_BASE_URL).replace(/\/$/, "");
  if (!base) return null;
  try {
    const url = new URL(base);
    const host = url.hostname.toLowerCase();
    if (host === "localhost" || host === "127.0.0.1" || host.endsWith(".localhost")) return null;
    return `${base}/api/webhooks/mercado-pago`;
  } catch {
    return null;
  }
}

export async function POST(req: Request) {
  const a = await acesso();
  if ("erro" in a) return a.erro;
  const { user, ctx, supabase } = a;

  const corpo = (await req.json().catch(() => null)) as Record<string, unknown> | null;
  const valor = Math.round(Number(corpo?.amount) * 100) / 100;
  if (!Number.isFinite(valor) || valor < 0.01) {
    return NextResponse.json({ error: "Informe um valor maior que zero para o Pix." }, { status: 400 });
  }

  const ownerId = ctx.ownerId || user.id;
  const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];

  let loja: Awaited<ReturnType<typeof credenciaisDaLoja>>;
  try {
    loja = await credenciaisDaLoja(ownerId);
  } catch (erro) {
    return NextResponse.json({ error: erro instanceof Error ? erro.message : "Mercado Pago indisponível." }, { status: 400 });
  }
  const { admin, integracao, credenciais } = loja;

  /* Quem paga. Com cliente escolhido, vai o e-mail e o documento dele; sem
     cliente, um endereço técnico da loja — o provedor exige um e-mail, e a
     venda de balcão não tem. */
  const clientId = typeof corpo?.client_id === "string" && isUuid(corpo.client_id) ? corpo.client_id : null;
  let nome = "Cliente";
  let email = "";
  let documento = "";
  if (clientId) {
    const { data } = await supabase
      .from("clients")
      .select("id,name,company_name,email,document")
      .eq("id", clientId)
      .in("user_id", tenantIds)
      .maybeSingle();
    const cliente = data as { name?: string | null; company_name?: string | null; email?: string | null; document?: string | null } | null;
    if (cliente) {
      nome = texto(cliente.company_name || cliente.name) || nome;
      email = texto(cliente.email).toLowerCase();
      documento = texto(cliente.document).replace(/\D/g, "");
    }
  }
  if (!emailValido(email)) email = `pdv-${ownerId.replace(/-/g, "").slice(0, 12)}@ktalogu.com.br`;

  const { data: perfil } = await admin
    .from("profiles")
    .select("catalog_name,store_name")
    .eq("id", ownerId)
    .maybeSingle();
  const nomeDaLoja = texto((perfil as { catalog_name?: string | null; store_name?: string | null } | null)?.catalog_name) ||
    texto((perfil as { store_name?: string | null } | null)?.store_name);
  const descricao = texto(corpo?.description) || (nomeDaLoja ? `Compra no balcão — ${nomeDaLoja}` : "Compra no balcão");
  const foto = lerFoto(corpo?.snapshot);

  /* A linha nasce antes da chamada: o id dela é a chave de idempotência, e se
     o provedor responder e a rede cair no caminho de volta, a cobrança não
     fica órfã. */
  const { data: criada, error: erroAoCriar } = await admin
    .from("pdv_pix_charges")
    .insert({
      company_id: ctx.companyId,
      tenant_id: ownerId,
      integration_id: integracao.id,
      provider: loja.nome,
      status: "pending",
      amount: valor,
      description: descricao,
      client_id: clientId,
      payer_email: email,
      environment: credenciais.ambiente,
      snapshot: foto ? { payload: foto.payload, resumo: foto.resumo, guardada_em: new Date().toISOString() } : null,
      created_by: user.id,
    })
    .select("id")
    .single();
  if (erroAoCriar || !criada?.id) {
    return NextResponse.json({ error: erroAoCriar?.message || "Não foi possível registrar a cobrança." }, { status: 500 });
  }
  const cobrancaId = String(criada.id);

  const vence = new Date(Date.now() + VALIDADE_MINUTOS * 60 * 1000);
  const referencia = `pdv:${ownerId}:cobranca:${cobrancaId}`;
  const partes = nome.split(/\s+/).filter(Boolean);

  /* O QR no balcao, venha do provedor que vier. Os dois ramos terminam neste
     formato, e o registro local e gravado uma vez so, logo abaixo. */
  type QrDoBalcao = {
    id: string;
    estado: string;
    providerStatus: string | null;
    detalhe: string | null;
    qrCode: string | null;
    qrCodeBase64: string | null;
    ticketUrl: string | null;
    expiraEm: string | null;
    bruto: Record<string, unknown>;
  };

  let gerada: QrDoBalcao;

  if (loja.nome === "asaas") {
    try {
      /* O Asaas nao cobra de um e-mail solto. No balcao quase nunca ha
         documento — o cliente esta ali, paga e vai embora —, entao sem CPF a
         cobranca nem sai. Por isso o documento e exigido antes de chamar. */
      if (documento.length !== 11 && documento.length !== 14) {
        throw new Error("Para cobrar Pix pelo Asaas, informe o CPF ou CNPJ do cliente.");
      }

      const clienteId = await clienteDoAsaas(loja.credenciais, {
        documento,
        nome: nome || "Cliente do balcão",
        email,
        referencia: clientId || undefined,
      });

      /* Vence hoje: Pix de balcao e pago na hora. A janela real de dez minutos
         e nossa, e quem a aplica e a tela — o provedor nao tem vencimento em
         minutos, so em dias. */
      const criadaNoAsaas = await chamarAsaas<CobrancaDoAsaas>("/payments", loja.credenciais, {
        method: "POST",
        body: JSON.stringify({
          customer: clienteId,
          billingType: "PIX",
          value: valor,
          dueDate: vencimentoNoBrasil(0),
          description: descricao,
          externalReference: referencia,
        }),
      });

      const idNoAsaas = texto(criadaNoAsaas.id);
      if (!idNoAsaas) throw new Error("O Asaas não devolveu a cobrança criada.");

      /* Sem QR na tela nao ha balcao: a cobranca criada nao serve de nada aqui,
         diferente da loja, onde sobra a fatura para o cliente abrir. */
      const qr = await qrDoPix(loja.credenciais, idNoAsaas);
      if (!qr) throw new Error("O Asaas criou a cobrança mas não devolveu o QR do Pix.");
      const qrCode = qr.copiaECola;
      const qrCodeBase64 = qr.imagemBase64;

      gerada = {
        id: idNoAsaas,
        estado: estadoDoAsaas(criadaNoAsaas.status),
        providerStatus: texto(criadaNoAsaas.status) || null,
        detalhe: null,
        qrCode,
        qrCodeBase64,
        ticketUrl: texto(criadaNoAsaas.invoiceUrl) || null,
        /* A validade e a nossa, de dez minutos. */
        expiraEm: vence.toISOString(),
        bruto: criadaNoAsaas,
      };
    } catch (erro) {
      const mensagem = erro instanceof Error ? erro.message : "O Asaas recusou a cobrança.";
      await admin
        .from("pdv_pix_charges")
        .update({ status: "failed", provider_status_detail: mensagem, updated_at: new Date().toISOString() })
        .eq("id", cobrancaId);
      return NextResponse.json({ error: mensagem }, { status: 400 });
    }
  } else {

  const aviso = enderecoDoAviso();
  const pedido: Record<string, unknown> = {
    transaction_amount: valor,
    description: descricao,
    payment_method_id: "pix",
    date_of_expiration: vencimentoParaOProvedor(vence),
    external_reference: referencia,
    payer: {
      email,
      first_name: partes[0] || "Cliente",
      last_name: partes.slice(1).join(" ") || "Balcão",
      ...(documento.length === 11 || documento.length === 14
        ? { identification: { type: documento.length === 14 ? "CNPJ" : "CPF", number: documento } }
        : {}),
    },
    metadata: { origem: "pdv", cobranca_id: cobrancaId, empresa: ctx.companyId },
  };
  if (aviso) pedido.notification_url = aviso;
  if (loja.config.statement_descriptor) pedido.statement_descriptor = loja.config.statement_descriptor;

  let resposta: RespostaDoProvedor;
  try {
    resposta = await chamarMercadoPago<RespostaDoProvedor>("/v1/payments", loja.credenciais.accessToken, {
      method: "POST",
      body: JSON.stringify(pedido),
      chaveDeIdempotencia: cobrancaId,
    });
  } catch (erro) {
    const mensagem = erro instanceof Error ? erro.message : "O Mercado Pago recusou a cobrança.";
    await admin
      .from("pdv_pix_charges")
      .update({ status: "failed", provider_status_detail: mensagem, updated_at: new Date().toISOString() })
      .eq("id", cobrancaId);
    return NextResponse.json({ error: mensagem }, { status: 400 });
  }

  const qr = dadosDoQr(resposta);
  gerada = {
    id: texto(resposta.id),
    estado: estadoDoPagamento(resposta.status),
    providerStatus: texto(resposta.status) || null,
    detalhe: texto(resposta.status_detail) || null,
    qrCode: qr.qrCode,
    qrCodeBase64: qr.qrCodeBase64,
    ticketUrl: qr.ticketUrl,
    expiraEm: texto(resposta.date_of_expiration) || vence.toISOString(),
    bruto: resposta,
  };

  }

  const estado = gerada.estado;
  const agora = new Date().toISOString();
  const { data: atualizada, error: erroAoAtualizar } = await admin
    .from("pdv_pix_charges")
    .update({
      status: estado === "approved" ? "approved" : estado === "pending" ? "pending" : estado === "rejected" ? "rejected" : "cancelled",
      provider_payment_id: gerada.id || null,
      provider_status: gerada.providerStatus,
      provider_status_detail: gerada.detalhe,
      external_reference: referencia,
      idempotency_key: cobrancaId,
      qr_code: gerada.qrCode,
      qr_code_base64: gerada.qrCodeBase64,
      ticket_url: gerada.ticketUrl,
      expires_at: gerada.expiraEm,
      approved_at: estado === "approved" ? agora : null,
      checked_at: agora,
      raw_response: gerada.bruto,
      updated_at: agora,
    })
    .eq("id", cobrancaId)
    .select("*")
    .single();

  if (erroAoAtualizar || !atualizada) {
    return NextResponse.json({ error: erroAoAtualizar?.message || "Cobrança criada, mas não foi possível guardá-la." }, { status: 500 });
  }

  const linha = atualizada as LinhaDaCobranca;
  if (!linha.qr_code && !linha.qr_code_base64) {
    return NextResponse.json({ error: "O Mercado Pago não devolveu o QR do Pix. Confira as chaves da integração." }, { status: 400 });
  }

  return NextResponse.json({ ok: true, cobranca: montarCobranca(linha) });
}

/* Quanto tempo uma cobrança paga sem venda continua sendo lembrada ao balcão.
   Uma semana: o Pix de sexta à noite ainda aparece na segunda de manhã. */
const JANELA_DAS_PENDENTES_HORAS = 24 * 7;

export async function GET(req: Request) {
  const a = await acesso();
  if ("erro" in a) return a.erro;
  const { ctx } = a;

  const url = new URL(req.url);
  const admin = createAdminClient();

  /*
    O Pix que caiu e a venda que não gravou. Aparece ao abrir o PDV, para
    ninguém descobrir dias depois que o dinheiro entrou e o pedido não existe.
    De passagem, as cobranças desta empresa que venceram sem ninguém perguntar
    por elas são marcadas como vencidas.
  */
  if (url.searchParams.get("pendentes") === "1") {
    const agora = new Date().toISOString();
    await admin
      .from("pdv_pix_charges")
      .update({ status: "expired", cancelled_at: agora, updated_at: agora })
      .eq("company_id", ctx.companyId)
      .eq("status", "pending")
      .lt("expires_at", new Date(Date.now() - 2 * 60 * 1000).toISOString());

    const desde = new Date(Date.now() - JANELA_DAS_PENDENTES_HORAS * 60 * 60 * 1000).toISOString();
    const { data } = await admin
      .from("pdv_pix_charges")
      .select("*")
      .eq("company_id", ctx.companyId)
      .eq("status", "approved")
      .is("order_id", null)
      .gte("approved_at", desde)
      .order("approved_at", { ascending: false })
      .limit(10);

    return NextResponse.json({ ok: true, cobrancas: ((data as LinhaDaCobranca[] | null) || []).map(montarCobranca) });
  }

  const id = texto(url.searchParams.get("id"));
  if (!id || !isUuid(id)) return NextResponse.json({ error: "Cobrança inválida." }, { status: 400 });

  const { data } = await admin.from("pdv_pix_charges").select("*").eq("id", id).eq("company_id", ctx.companyId).maybeSingle();
  let linha = data as LinhaDaCobranca | null;
  if (!linha) return NextResponse.json({ error: "Cobrança não encontrada." }, { status: 404 });

  const ultimaConferencia = linha.checked_at ? new Date(linha.checked_at).getTime() : 0;
  const deveConferir =
    linha.status === "pending" &&
    Boolean(linha.provider_payment_id) &&
    Date.now() - ultimaConferencia >= INTERVALO_DE_CONFERENCIA_MS;

  if (deveConferir) {
    try {
      const loja = await credenciaisDaLoja(linha.tenant_id);
      const remoto = await lerNoProvedor(loja, String(linha.provider_payment_id));
      const estado = remoto.estado;
      const agora = new Date().toISOString();
      const detalhe = remoto.detalhe;

      /*
        A janela de dez minutos e NOSSA, e so o Mercado Pago a conhece.

        Ele recebe `date_of_expiration` e vence o Pix sozinho. O Asaas nao tem
        vencimento em minutos, so em dias: para ele a cobranca segue de pe
        depois da tela fechar. Sem este cancelamento, o cliente que pagasse
        duas horas depois mandaria dinheiro para uma venda que nunca existiu.
      */
      const passouDaHora =
        estado === "pending" && Boolean(linha.expires_at) && new Date(linha.expires_at as string).getTime() < Date.now();

      if (passouDaHora && loja.nome === "asaas") {
        try {
          await cancelarNoProvedor(loja, String(linha.provider_payment_id));
        } catch {
          /* Se o cancelamento falhar, o estado local ainda fecha: o pior caso
             e uma cobranca orfa no painel do Asaas, e nao um pagamento solto. */
        }
      }

      /* O Mercado Pago cancela sozinho o Pix vencido e escreve "expired" no
         detalhe: aqui isso vira o estado próprio, para a tela dizer a coisa
         certa. */
      const novoStatus =
        passouDaHora
          ? "expired"
          : estado === "approved"
            ? "approved"
            : estado === "pending"
              ? "pending"
              : estado === "rejected"
                ? "rejected"
                : detalhe.toLowerCase().includes("expired")
                  ? "expired"
                  : "cancelled";
      /* Só grava se a cobrança ainda estiver pendente: a conferência demora
         um segundo no provedor, e nesse segundo o caixa pode ter cancelado.
         Sem esta condição, a resposta atrasada desfazia o cancelamento. */
      const { data: depois } = await admin
        .from("pdv_pix_charges")
        .update({
          status: novoStatus,
          provider_status: remoto.providerStatus,
          provider_status_detail: detalhe || null,
          approved_at: novoStatus === "approved" ? remoto.aprovadoEm || agora : linha.approved_at,
          cancelled_at: novoStatus === "cancelled" || novoStatus === "expired" ? agora : null,
          checked_at: agora,
          raw_response: remoto.bruto,
          updated_at: agora,
        })
        .eq("id", linha.id)
        .eq("status", "pending")
        .select("*")
        .maybeSingle();
      if (depois) {
        linha = depois as LinhaDaCobranca;
      } else {
        const { data: atual } = await admin.from("pdv_pix_charges").select("*").eq("id", linha.id).maybeSingle();
        if (atual) linha = atual as LinhaDaCobranca;
      }
    } catch {
      /* Sem resposta do provedor agora, fica o que já estava; a tela pergunta
         de novo em quatro segundos. */
    }
  }

  return NextResponse.json({ ok: true, cobranca: montarCobranca(linha) });
}

export async function DELETE(req: Request) {
  const a = await acesso();
  if ("erro" in a) return a.erro;
  const { ctx } = a;

  const id = texto(new URL(req.url).searchParams.get("id"));
  if (!id || !isUuid(id)) return NextResponse.json({ error: "Cobrança inválida." }, { status: 400 });

  const admin = createAdminClient();
  const { data } = await admin.from("pdv_pix_charges").select("*").eq("id", id).eq("company_id", ctx.companyId).maybeSingle();
  const linha = data as LinhaDaCobranca | null;
  if (!linha) return NextResponse.json({ error: "Cobrança não encontrada." }, { status: 404 });

  if (linha.status === "approved") {
    return NextResponse.json({ error: "Este Pix já foi pago; não dá mais para cancelar." }, { status: 400 });
  }
  if (linha.status !== "pending") {
    return NextResponse.json({ ok: true, cobranca: montarCobranca(linha) });
  }

  if (linha.provider_payment_id) {
    try {
      const loja = await credenciaisDaLoja(linha.tenant_id);
      await cancelarNoProvedor(loja, linha.provider_payment_id);
    } catch {
      /* O provedor pode já ter cancelado (vencido) ou estar fora do ar. O
         registro local fecha do mesmo jeito: a tela não pode ficar presa. */
    }
  }

  /* Mesma regra da conferência, ao contrário: se o pagamento caiu no mesmo
     instante em que o caixa cancelou, quem manda é o dinheiro. A tela recebe
     a cobrança aprovada e segue o caminho do Pix pago. */
  const agora = new Date().toISOString();
  const { data: depois } = await admin
    .from("pdv_pix_charges")
    .update({ status: "cancelled", cancelled_at: agora, updated_at: agora })
    .eq("id", linha.id)
    .eq("status", "pending")
    .select("*")
    .maybeSingle();
  if (depois) {
    return NextResponse.json({ ok: true, cobranca: montarCobranca(depois as LinhaDaCobranca) });
  }
  const { data: atual } = await admin.from("pdv_pix_charges").select("*").eq("id", linha.id).maybeSingle();
  return NextResponse.json({ ok: true, cobranca: montarCobranca((atual as LinhaDaCobranca | null) || linha) });
}
