import { NextResponse } from "next/server";
import { autenticar } from "@/lib/orders/painel-de-producao.server";
import { dividirCusto } from "@/lib/precificacao/equilibrio";
import { GET as saudeGET } from "@/app/api/precificacao/saude/route";

/**
 * A análise de um pedido: quanto ele deixa e quanto disso paga as contas do
 * mês.
 *
 * A régua é a mesma da página Ponto de Equilíbrio: contribuição = receita −
 * matéria-prima − variáveis da venda (impostos, taxas, perda). A mão de obra
 * fica dentro da contribuição, porque é ela que paga o fixo. O termômetro do
 * mês vem da própria rota de saúde, chamada aqui dentro, para os dois lugares
 * nunca discordarem.
 */

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

type ItemRow = {
  id: string;
  title: string;
  quantity: number | string | null;
  unit_price: number | string | null;
  total_price: number | string | null;
  product_id: string | null;
  calculated_cost: number | string | null;
  pricing_snapshot: unknown;
};

function custoDoItem(item: ItemRow): { custo: number | null; origem: string | null } {
  const snap = item.pricing_snapshot && typeof item.pricing_snapshot === "object" ? (item.pricing_snapshot as Record<string, unknown>) : null;
  if (snap && Number.isFinite(Number(snap.custo_total))) {
    return { custo: Number(snap.custo_total), origem: typeof snap.origem_do_custo === "string" ? snap.origem_do_custo : null };
  }
  const unitario = Number(item.calculated_cost);
  if (item.calculated_cost !== null && Number.isFinite(unitario) && unitario > 0) {
    return { custo: duasCasas(unitario * (Number(item.quantity) || 0)), origem: "calculado" };
  }
  return { custo: null, origem: null };
}

export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
  try {
    const { id } = await params;
    const auth = await autenticar("orders.read");
    if (auth.erro) return auth.erro;
    const { admin, tenantIds, companyId } = auth.contexto;

    const [pedidoR, itensR, variaveisR] = await Promise.all([
      admin
        .from("orders")
        .select("id, total_amount, created_at, quotes:quotes!quote_id(subtotal, discount, shipping)")
        .eq("id", id)
        .in("user_id", tenantIds)
        .maybeSingle(),
      admin
        .from("order_items")
        .select("id, title, quantity, unit_price, total_price, product_id, calculated_cost, pricing_snapshot")
        .eq("order_id", id),
      admin.rpc("custo_variavel_da_venda", { p_company_id: companyId }),
    ]);
    if (pedidoR.error) return NextResponse.json({ error: pedidoR.error.message }, { status: 400 });
    if (!pedidoR.data) return NextResponse.json({ error: "Pedido não encontrado." }, { status: 404 });
    if (itensR.error) return NextResponse.json({ error: itensR.error.message }, { status: 400 });

    const itens = (itensR.data || []) as unknown as ItemRow[];
    const quote = (pedidoR.data as { quotes?: { subtotal?: number | null; discount?: number | null; shipping?: number | null } | null }).quotes;
    const desconto = Math.max(0, Number(quote?.discount ?? 0) || 0);
    const somaDosItens = itens.reduce((acc, it) => acc + (Number(it.total_price) || Number(it.quantity) * Number(it.unit_price) || 0), 0);
    const receita = duasCasas(Math.max(0, somaDosItens - desconto));
    const variaveisPct = Number((variaveisR.data as { total_pct?: number } | null)?.total_pct ?? 0) || 0;

    /* A ficha do produto divide o custo em matéria-prima e processos, na
       mesma proporção da página de saúde. */
    const idsDeProdutos = [...new Set(itens.map((i) => i.product_id).filter((v): v is string => Boolean(v)))];
    const [matR, procR] = idsDeProdutos.length
      ? await Promise.all([
          admin.from("product_materials").select("product_id, cost_total").in("product_id", idsDeProdutos),
          admin.from("product_processes").select("product_id, cost_total").in("product_id", idsDeProdutos),
        ])
      : [{ data: [] }, { data: [] }];
    const ficha = new Map<string, { materiais: number; processos: number }>();
    for (const l of (matR.data || []) as { product_id: string; cost_total: number | null }[]) {
      const atual = ficha.get(l.product_id) ?? { materiais: 0, processos: 0 };
      atual.materiais += Number(l.cost_total) || 0;
      ficha.set(l.product_id, atual);
    }
    for (const l of (procR.data || []) as { product_id: string; cost_total: number | null }[]) {
      const atual = ficha.get(l.product_id) ?? { materiais: 0, processos: 0 };
      atual.processos += Number(l.cost_total) || 0;
      ficha.set(l.product_id, atual);
    }

    const linhas = itens.map((it) => {
      const { custo, origem } = custoDoItem(it);
      const f = it.product_id ? ficha.get(it.product_id) : undefined;
      const partes = dividirCusto(custo ?? 0, f?.materiais ?? 0, f?.processos ?? 0);
      const receitaItem = duasCasas(Number(it.total_price) || Number(it.quantity) * Number(it.unit_price) || 0);
      return {
        id: it.id,
        title: it.title,
        receita: receitaItem,
        custo,
        materiais: custo === null ? null : partes.materiais,
        origem,
      };
    });
    const custoConhecido = linhas.length > 0 && linhas.every((l) => l.custo !== null);
    const custoTotal = duasCasas(linhas.reduce((acc, l) => acc + (l.custo ?? 0), 0));
    const materiaPrima = duasCasas(linhas.reduce((acc, l) => acc + (l.materiais ?? 0), 0));
    const variaveisValor = duasCasas((receita * variaveisPct) / 100);
    const contribuicao = duasCasas(receita - materiaPrima - variaveisValor);
    const lucro = custoConhecido ? duasCasas(receita - custoTotal - variaveisValor) : null;

    /* O termômetro do mês, da própria rota de saúde. */
    let mes: Record<string, unknown> | null = null;
    let erroDoMes: string | null = null;
    try {
      const url = new URL("/api/precificacao/saude?dias=30", req.url);
      const res = await saudeGET(new Request(url.toString(), { headers: req.headers }));
      const json = (await res.json().catch(() => null)) as
        | { mes?: Record<string, unknown>; capacidade?: { horasDisponiveisMes?: number }; error?: string }
        | null;
      if (!res.ok || !json || json.error) {
        erroDoMes = res.status === 403 ? "Sem permissão para ver a precificação." : json?.error || "Não deu para ler o mês.";
      } else if (json.mes) {
        const m = json.mes;
        const fixo = Number(m.fixoDoMes ?? 0) || 0;
        const cmAcumulada = Number(m.cm ?? 0) || 0;
        const horas = Number(json.capacidade?.horasDisponiveisMes ?? 0) || 0;
        const custoFixoPorHora = fixo > 0 && horas > 0 ? duasCasas(fixo / horas) : null;
        const falta = duasCasas(Math.max(0, fixo - cmAcumulada));
        mes = {
          temFixo: fixo > 0,
          fixoMensal: duasCasas(fixo),
          cmAcumulada: duasCasas(cmAcumulada),
          receitaMes: duasCasas(Number(m.receita ?? 0) || 0),
          progressoPct: duasCasas(Number(m.progressoPct ?? 0) || 0),
          bateu: Boolean(m.bateu),
          lucroAtual: duasCasas(Number(m.lucroAtual ?? 0) || 0),
          diasRestantes: Number(m.diasRestantes ?? 0) || 0,
          faltaParaEquilibrio: falta,
          participacaoDoPedidoPct: fixo > 0 ? duasCasas((contribuicao / fixo) * 100) : null,
          pedidosComoEste: contribuicao > 0 && falta > 0 ? Math.ceil(falta / contribuicao) : null,
          custoFixoPorHora,
          horasQueOPedidoPaga: custoFixoPorHora && contribuicao > 0 ? duasCasas(contribuicao / custoFixoPorHora) : null,
        };
      }
    } catch (e) {
      erroDoMes = e instanceof Error ? e.message : "Não deu para ler o mês.";
    }

    return NextResponse.json({
      ok: true,
      pedido: {
        receita,
        desconto,
        custoTotal,
        custoConhecido,
        materiaPrima,
        variaveisPct: duasCasas(variaveisPct),
        variaveisValor,
        contribuicao,
        contribuicaoPct: receita > 0 ? duasCasas((contribuicao / receita) * 100) : 0,
        lucro,
      },
      itens: linhas,
      mes,
      erroDoMes,
    });
  } catch (error) {
    const message = error instanceof Error ? error.message : "Erro ao analisar o pedido.";
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
