import { NextResponse } from "next/server";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { numeroDoTexto } from "@/lib/numero-do-campo";

/**
 * Receber a mercadoria de uma compra.
 *
 * Antes disto, registrar uma compra nunca tocava no estoque nem no custo: o
 * status "Recebido" era um rótulo. Agora este é o ponto em que o pedido de
 * compra e o insumo se encontram.
 *
 * A rota faz o que é da alçada dela, permissão e dono, e entrega o resto para
 * `receber_compra_do_fornecedor`. As cinco escritas do recebimento (quantidade
 * recebida, entrada de estoque com custo médio, último preço do item, status
 * da compra e valor da conta a pagar) precisam valer juntas ou nenhuma, e isso
 * só existe dentro de uma transação do banco.
 */

type Params = { params: Promise<{ id: string }> };

/* `celula_id`: no estoque novo, a célula da grade em que a linha entra (a
   mesma linha pode vir mais de uma vez, uma por célula). Fora do v2 a tela
   não manda e a função ignora. */
type ItemRecebido = { item_id: string; quantidade: number; preco_unitario: number | null; celula_id?: string };

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

function texto(valor: unknown) {
  return typeof valor === "string" ? valor.trim() : "";
}

function numeroOuNulo(valor: unknown) {
  if (valor === null || valor === undefined || valor === "") return null;
  const n = typeof valor === "number" ? valor : numeroDoTexto(String(valor));
  return Number.isFinite(n) && n >= 0 ? n : null;
}

export async function POST(req: Request, { params }: Params) {
  const supabase = await createClient();
  const {
    data: { user },
    error: authError,
  } = await supabase.auth.getUser();
  if (authError || !user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "suppliers.list.write")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const { id } = await params;
  const body = (await req.json().catch(() => null)) as
    | { purchaseOrderId?: unknown; itens?: unknown; observacao?: unknown }
    | null;

  const purchaseOrderId = texto(body?.purchaseOrderId);
  if (!purchaseOrderId) return NextResponse.json({ error: "Compra inválida." }, { status: 400 });

  const brutos = Array.isArray(body?.itens) ? body.itens : [];
  const itens: ItemRecebido[] = [];
  for (const bruto of brutos) {
    const linha = bruto as Record<string, unknown>;
    const itemId = texto(linha?.itemId);
    const quantidade = numeroOuNulo(linha?.quantidade);
    if (!itemId || !quantidade || quantidade <= 0) continue;
    const celulaId = texto(linha?.celulaId);
    itens.push({
      item_id: itemId,
      quantidade,
      preco_unitario: numeroOuNulo(linha?.precoUnitario),
      ...(celulaId && UUID.test(celulaId) ? { celula_id: celulaId } : {}),
    });
  }

  if (itens.length === 0) {
    return NextResponse.json({ error: "Informe a quantidade que chegou de pelo menos um item." }, { status: 400 });
  }

  const admin = createAdminClient();

  /* O dono confere aqui: a função do banco recebe a empresa pronta e não sabe
     de sessão. */
  const { data: supplier } = await admin
    .from("suppliers")
    .select("id")
    .eq("company_id", ctx.companyId)
    .eq("id", id)
    .is("deleted_at", null)
    .maybeSingle();

  if (!supplier?.id) return NextResponse.json({ error: "Fornecedor não encontrado." }, { status: 404 });

  const { data: compra } = await admin
    .from("supplier_purchase_orders")
    .select("id")
    .eq("company_id", ctx.companyId)
    .eq("supplier_id", id)
    .eq("id", purchaseOrderId)
    .maybeSingle();

  if (!compra?.id) return NextResponse.json({ error: "Compra não encontrada." }, { status: 404 });

  const { data, error } = await admin.rpc("receber_compra_do_fornecedor", {
    p_company: ctx.companyId,
    p_compra: purchaseOrderId,
    p_itens: itens,
    p_ator: user.id,
    p_observacao: texto(body?.observacao) || null,
  });

  if (error) {
    /* A função levanta mensagens já escritas para o usuário ("Compra cancelada
       não recebe mercadoria."). Repassar é melhor do que trocar por um genérico. */
    return NextResponse.json({ error: error.message || "Falha ao receber a compra." }, { status: 400 });
  }

  const resultado = (data ?? {}) as Record<string, unknown>;

  /* O rastro deste recebimento nao e escrito aqui de proposito.
     Quem grava `purchase.received` no historico do fornecedor e a propria
     `receber_compra_do_fornecedor`, dentro da transacao. Escrever tambem
     daqui criava DOIS eventos por recebimento, e um deles poderia
     sobreviver a uma transacao que falhou. */

  return NextResponse.json({
    ok: true,
    status: String(resultado.status || "partial"),
    itensRecebidos: Number(resultado.itensRecebidos || 0),
    entradasNoEstoque: Number(resultado.entradasNoEstoque || 0),
    total: Number(resultado.total || 0),
    subtotal: Number(resultado.subtotal || 0),
    /* Quanto de frete e imposto (menos desconto) a compra carrega, e o fator
       que isso virou no custo de cada insumo. A tela usa para dizer, na hora
       do recebimento, por que o custo subiu mais do que a nota. */
    extras: Number(resultado.extras || 0),
    fatorDeRateio: Number(resultado.fatorDeRateio ?? 1),
    contaAjustada: resultado.contaAjustada === true,
    contaJaPaga: resultado.contaJaPaga === true,
  });
}
