import { NextResponse } from "next/server";
import { MOTIVOS_DE_PERDA, ROTULO_DO_MOTIVO, type MotivoDePerda } from "@/lib/orders/eventos-de-producao";
import { avisarProducao } from "@/lib/orders/avisos-de-producao.server";
import {
  autenticar,
  carregarItens,
  carregarPedido,
  montarPainel,
  type Contexto,
  type OrderRow,
} from "@/lib/orders/painel-de-producao.server";
import { montarFichasDoPedido } from "@/lib/orders/ficha-do-pedido.server";

/**
 * Perdas e retrabalho de um pedido.
 *
 *   POST { acao: "registrar", order_item_id, material_id?, quantidade, motivo, notas?, refeito? }
 *        Quem produz registra: o insumo (ou a peça pronta), quanto, por quê.
 *        Baixa o estoque na hora — o material já saiu da prateleira — a menos
 *        que a empresa exija aprovação acima de um percentual do consumo
 *        previsto; aí fica pendente. Em ambos os casos avisa quem responde.
 *
 *   POST { acao: "aprovar" | "recusar" | "estornar", perda_id }
 *        Quem cuida do estoque decide. Aprovar baixa; estornar devolve pelo
 *        extrato, nunca apagando.
 *
 * Devolve o painel de produção atualizado.
 */

const duasCasas = (n: number) => Math.round(n * 100) / 100;

async function anotarNoHistorico(contexto: Contexto, order: OrderRow, nota: string) {
  await contexto.supabase.from("order_history").insert({
    order_id: order.id,
    user_id: contexto.userId,
    status_from: order.status,
    status_to: order.status,
    notes: nota,
  });
}

export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
  try {
    const { id } = await params;
    const body = (await req.json().catch(() => null)) as Record<string, unknown> | null;
    const acao = typeof body?.acao === "string" ? body.acao : "";

    if (acao === "registrar") {
      const auth = await autenticar("production.board.write");
      if (auth.erro) return auth.erro;
      const { contexto } = auth;

      const orderItemId = typeof body?.order_item_id === "string" ? body.order_item_id : "";
      const materialId = typeof body?.material_id === "string" && body.material_id ? body.material_id : null;
      const quantidade = duasCasas(Number(body?.quantidade));
      const motivo = typeof body?.motivo === "string" ? (body.motivo as MotivoDePerda) : "outro";
      const notas = typeof body?.notas === "string" ? body.notas.trim().slice(0, 500) : "";
      const refeito = body?.refeito === true;

      if (!orderItemId) return NextResponse.json({ error: "Informe o item." }, { status: 400 });
      if (!(quantidade > 0)) return NextResponse.json({ error: "Informe a quantidade perdida." }, { status: 400 });
      if (!MOTIVOS_DE_PERDA.includes(motivo)) return NextResponse.json({ error: "Motivo inválido." }, { status: 400 });

      const pedido = await carregarPedido(contexto, id);
      if (pedido.erro) return pedido.erro;
      const itens = await carregarItens(contexto, id);
      if (itens.erro) return itens.erro;
      const item = itens.items.find((i) => i.id === orderItemId);
      if (!item) return NextResponse.json({ error: "Item não pertence a este pedido." }, { status: 404 });

      /* O insumo, da ficha do item: nome, unidade e o consumo previsto que
         serve de régua para a aprovação. Sem insumo, é a peça pronta. */
      const { fichas } = await montarFichasDoPedido(contexto.admin, [item], contexto.tenantIds);
      const ficha = fichas[0]?.ficha;
      const insumo = materialId ? ficha?.insumos.find((i) => i.materialId === materialId) ?? null : null;
      if (materialId && !insumo) {
        return NextResponse.json({ error: "Este insumo não está na ficha do item." }, { status: 400 });
      }
      const materialNome = insumo ? insumo.nome : "Peça pronta";
      const unidade = insumo ? insumo.unidade : "un";
      const previsto = insumo ? insumo.quantidade : Number(item.quantity) || 0;

      const { data: perfil } = await contexto.admin
        .from("profiles")
        .select("production_loss_approval_pct")
        .eq("id", pedido.order.user_id)
        .maybeSingle();
      const limiteRaw = perfil?.production_loss_approval_pct;
      const limitePct =
        limiteRaw === null || limiteRaw === undefined || !Number.isFinite(Number(limiteRaw)) ? null : Number(limiteRaw);
      const exigeAprovacao =
        limitePct !== null && (limitePct <= 0 || previsto <= 0 || quantidade > (previsto * limitePct) / 100);

      const { data: criada, error: criarErro } = await contexto.admin
        .from("order_production_losses")
        .insert({
          user_id: pedido.order.user_id,
          order_id: id,
          order_item_id: orderItemId,
          item_title: item.title,
          material_id: materialId,
          material_name: materialNome,
          unit: unidade,
          quantity: quantidade,
          reason: motivo,
          notes: notas || null,
          redo: refeito,
          status: exigeAprovacao ? "pendente" : "aplicada",
          registered_by: contexto.userId,
        })
        .select("id")
        .single();
      if (criarErro) return NextResponse.json({ error: criarErro.message }, { status: 400 });

      if (!exigeAprovacao) {
        const { data: aplicado, error: aplicarErro } = await contexto.admin.rpc("aplicar_perda_de_producao", {
          p_loss_id: criada.id,
          p_user: contexto.userId,
        });
        if (aplicarErro) return NextResponse.json({ error: aplicarErro.message }, { status: 400 });
        const resultado = aplicado as { ok?: boolean; erro?: string } | null;
        if (resultado && resultado.ok === false) {
          return NextResponse.json({ error: resultado.erro || "Não foi possível aplicar a perda." }, { status: 400 });
        }
      }

      const descricao = `${quantidade} ${unidade} de ${materialNome} · ${ROTULO_DO_MOTIVO[motivo].toLowerCase()}${
        refeito ? " · refeito" : ""
      }`;
      await anotarNoHistorico(
        contexto,
        pedido.order,
        exigeAprovacao
          ? `Perda registrada em "${item.title}": ${descricao}. Aguarda aprovação para baixar o estoque.`
          : `Perda registrada em "${item.title}": ${descricao}.`
      );
      /* O aviso não segura a resposta: quem registrou já fez a parte dele. */
      void avisarProducao(contexto.admin, {
        tenantIds: contexto.tenantIds,
        exceto: contexto.userId,
        titulo: exigeAprovacao
          ? `Perda aguardando aprovação no pedido #${pedido.order.number}`
          : `Perda registrada no pedido #${pedido.order.number}`,
        corpo: `${item.title}: ${descricao}`,
        rota: `/dashboard/orders/${id}`,
        tag: `perda-${id}`,
      }).catch(() => undefined);

      const painel = await montarPainel(contexto, pedido.order, itens.items);
      if (painel.erro) return painel.erro;
      return NextResponse.json(painel.painel);
    }

    if (acao === "aprovar" || acao === "recusar" || acao === "estornar") {
      const auth = await autenticar("materials.write");
      if (auth.erro) return auth.erro;
      const { contexto } = auth;

      const perdaId = typeof body?.perda_id === "string" ? body.perda_id : "";
      if (!perdaId) return NextResponse.json({ error: "Informe a perda." }, { status: 400 });

      const pedido = await carregarPedido(contexto, id);
      if (pedido.erro) return pedido.erro;

      const { data: perda, error: perdaErro } = await contexto.admin
        .from("order_production_losses")
        .select("id, status, item_title, material_name, unit, quantity")
        .eq("id", perdaId)
        .eq("order_id", id)
        .maybeSingle();
      if (perdaErro) return NextResponse.json({ error: perdaErro.message }, { status: 400 });
      if (!perda) return NextResponse.json({ error: "Perda não encontrada neste pedido." }, { status: 404 });

      const descricao = `${Number(perda.quantity)} ${perda.unit || "un"} de ${perda.material_name || "peça pronta"} em "${perda.item_title || "item"}"`;

      if (acao === "aprovar") {
        if (perda.status !== "pendente") return NextResponse.json({ error: "Esta perda não está pendente." }, { status: 409 });
        const { data, error } = await contexto.admin.rpc("aplicar_perda_de_producao", { p_loss_id: perdaId, p_user: contexto.userId });
        if (error) return NextResponse.json({ error: error.message }, { status: 400 });
        const r = data as { ok?: boolean; erro?: string } | null;
        if (r && r.ok === false) return NextResponse.json({ error: r.erro || "Não foi possível aprovar." }, { status: 400 });
        await anotarNoHistorico(contexto, pedido.order, `Perda aprovada: ${descricao}. Estoque baixado.`);
      } else if (acao === "recusar") {
        if (perda.status !== "pendente") return NextResponse.json({ error: "Esta perda não está pendente." }, { status: 409 });
        const { error } = await contexto.admin
          .from("order_production_losses")
          .update({ status: "recusada", reviewed_by: contexto.userId, reviewed_at: new Date().toISOString() })
          .eq("id", perdaId);
        if (error) return NextResponse.json({ error: error.message }, { status: 400 });
        await anotarNoHistorico(contexto, pedido.order, `Perda recusada: ${descricao}.`);
      } else {
        if (perda.status !== "aplicada") return NextResponse.json({ error: "Só uma perda aplicada pode ser estornada." }, { status: 409 });
        const { data, error } = await contexto.admin.rpc("estornar_perda_de_producao", { p_loss_id: perdaId, p_user: contexto.userId });
        if (error) return NextResponse.json({ error: error.message }, { status: 400 });
        const r = data as { ok?: boolean; erro?: string } | null;
        if (r && r.ok === false) return NextResponse.json({ error: r.erro || "Não foi possível estornar." }, { status: 400 });
        await anotarNoHistorico(contexto, pedido.order, `Perda estornada: ${descricao}. Estoque devolvido.`);
      }

      const itens = await carregarItens(contexto, id);
      if (itens.erro) return itens.erro;
      const painel = await montarPainel(contexto, pedido.order, itens.items);
      if (painel.erro) return painel.erro;
      return NextResponse.json(painel.painel);
    }

    return NextResponse.json({ error: "Ação inválida." }, { status: 400 });
  } catch (error) {
    const message = error instanceof Error ? error.message : "Erro ao registrar a perda.";
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
