import { planoValido } from "@/lib/orders/ficha-de-producao";
import { NextResponse } from "next/server";
import type { SupabaseClient } from "@supabase/supabase-js";
import { canCompany, getCompanyContext, type CompanyPermission } from "@/lib/company-context";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import { expedienteValido } from "./expediente";
import {
  resumirProducao,
  type EventoDeProducao,
  type MotivoDePerda,
  type PainelDeProducao,
  type PerdaRegistrada,
  type StatusDaPerda,
} from "./eventos-de-producao";
import { montarFichasDoPedido, type ItemDoPedidoParaFicha } from "./ficha-do-pedido.server";

/**
 * O painel de produção de um pedido, montado num lugar só.
 *
 * Duas rotas devolvem o painel — a de apontamento e a de perdas — e as duas
 * precisam do mesmo resultado depois de escrever: etapas, sessões do
 * cronômetro, apontamentos por item, perdas registradas e o previsto da
 * ficha. Montar aqui é o que garante que "concluir item" e "registrar perda"
 * devolvem a mesma tela.
 */

export type OrderRow = {
  id: string;
  number: number;
  status: string;
  user_id: string;
  production_started_at: string | null;
  production_accumulated_seconds: number | null;
  production_last_stopped_at: string | null;
  production_first_started_at: string | null;
  production_finished_at: string | null;
};

export type Contexto = {
  supabase: SupabaseClient;
  admin: SupabaseClient;
  tenantIds: string[];
  ownerId: string | null;
  companyId: string;
  userId: string;
  pode: (permissao: CompanyPermission) => boolean;
};

export type PerdaRow = {
  id: string;
  order_item_id: string | null;
  item_title: string | null;
  material_id: string | null;
  material_name: string | null;
  unit: string | null;
  quantity: number | string;
  reason: string;
  notes: string | null;
  redo: boolean;
  status: string;
  stock_movement_id: string | null;
  registered_by: string | null;
  registered_at: string;
  reviewed_by: string | null;
  reviewed_at: string | null;
};

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

  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, permissao)) {
    return { erro: NextResponse.json({ error: "Sem permissão." }, { status: 403 }) };
  }
  const contexto: Contexto = {
    supabase,
    admin: createAdminClient(),
    tenantIds: ctx.tenantIds?.length ? ctx.tenantIds : [user.id],
    ownerId: ctx.ownerId ?? null,
    companyId: ctx.companyId,
    userId: user.id,
    pode: (p) => canCompany(ctx, user.id, p),
  };
  return { contexto };
}

export async function carregarPedido(contexto: Contexto, id: string) {
  const { data, error } = await contexto.supabase
    .from("orders")
    .select(
      "id, number, status, user_id, production_started_at, production_accumulated_seconds, production_last_stopped_at, production_first_started_at, production_finished_at"
    )
    .eq("id", id)
    .in("user_id", contexto.tenantIds)
    .maybeSingle();
  if (error) return { erro: NextResponse.json({ error: error.message }, { status: 400 }) };
  if (!data) return { erro: NextResponse.json({ error: "Pedido não encontrado." }, { status: 404 }) };
  return { order: data as unknown as OrderRow };
}

export async function carregarItens(contexto: Contexto, orderId: string) {
  const { data, error } = await contexto.supabase
    .from("order_items")
    .select(
      "id, product_id, title, description, quantity, unit_type, width_m, height_m, length_m, selected_options, production_snapshot, production_minutes, production_plan, products(sku, barcode, production_templates)"
    )
    .eq("order_id", orderId)
    .order("created_at", { ascending: true });
  if (error) return { erro: NextResponse.json({ error: error.message }, { status: 400 }) };
  return { items: (data || []) as unknown as ItemDoPedidoParaFicha[] };
}

const STATUS_DA_PERDA: StatusDaPerda[] = ["aplicada", "pendente", "recusada", "estornada"];
const MOTIVOS: MotivoDePerda[] = ["impressao", "corte", "acabamento", "defeito_insumo", "cliente", "retrabalho", "outro"];

function perdaDaLinha(row: PerdaRow, nomeDe: (id: string) => string): PerdaRegistrada {
  return {
    id: row.id,
    orderItemId: row.order_item_id,
    itemTitle: row.item_title,
    materialId: row.material_id,
    materialNome: row.material_name || "Peça pronta",
    unidade: row.unit || "un",
    quantidade: Number(row.quantity) || 0,
    motivo: MOTIVOS.includes(row.reason as MotivoDePerda) ? (row.reason as MotivoDePerda) : "outro",
    notas: row.notes,
    refeito: Boolean(row.redo),
    status: STATUS_DA_PERDA.includes(row.status as StatusDaPerda) ? (row.status as StatusDaPerda) : "aplicada",
    registradoPor: row.registered_by,
    registradoPorNome: row.registered_by ? nomeDe(row.registered_by) : null,
    registradoEm: row.registered_at,
    revisadoPorNome: row.reviewed_by ? nomeDe(row.reviewed_by) : null,
    revisadoEm: row.reviewed_at,
    comMovimento: Boolean(row.stock_movement_id),
  };
}

export async function montarPainel(contexto: Contexto, order: OrderRow, items: ItemDoPedidoParaFicha[]) {
  const [eventosResult, perfilResult, fichasResult, perdasResult] = await Promise.all([
    contexto.admin
      .from("order_production_events")
      .select("id, kind, status, stage_id, stage_label, order_item_id, item_title, started_at, ended_at, started_by, ended_by, source")
      .eq("order_id", order.id)
      .order("started_at", { ascending: true }),
    contexto.admin
      .from("profiles")
      .select("production_work_schedule, production_loss_approval_pct")
      .eq("id", order.user_id)
      .maybeSingle(),
    montarFichasDoPedido(contexto.admin, items, contexto.tenantIds),
    contexto.admin
      .from("order_production_losses")
      .select("id, order_item_id, item_title, material_id, material_name, unit, quantity, reason, notes, redo, status, stock_movement_id, registered_by, registered_at, reviewed_by, reviewed_at")
      .eq("order_id", order.id)
      .order("registered_at", { ascending: true }),
  ]);

  if (eventosResult.error) return { erro: NextResponse.json({ error: eventosResult.error.message }, { status: 400 }) };
  if (fichasResult.error) return { erro: NextResponse.json({ error: fichasResult.error }, { status: 400 }) };
  if (perdasResult.error) return { erro: NextResponse.json({ error: perdasResult.error.message }, { status: 400 }) };

  const eventos = (eventosResult.data || []) as unknown as EventoDeProducao[];
  const perdasRows = (perdasResult.data || []) as unknown as PerdaRow[];
  const expediente = expedienteValido(perfilResult.data?.production_work_schedule);
  const limiteRaw = perfilResult.data?.production_loss_approval_pct;
  const perdasExigemAprovacaoAcimaPct =
    limiteRaw === null || limiteRaw === undefined || !Number.isFinite(Number(limiteRaw)) ? null : Number(limiteRaw);

  /* Quem fez: o nome do funcionário, ou "Empresa (Admin)" para o dono —
     o mesmo rótulo que o pedido usa para quem o criou. */
  const ids = Array.from(
    new Set(
      [
        ...eventos.flatMap((e) => [e.started_by, e.ended_by]),
        ...perdasRows.flatMap((p) => [p.registered_by, p.reviewed_by]),
      ].filter((v): v is string => Boolean(v))
    )
  );
  const nomes = new Map<string, string>();
  if (ids.length > 0) {
    const { data: perfis } = await contexto.admin
      .from("employee_profiles")
      .select("user_id, full_name")
      .in("user_id", ids);
    for (const p of (perfis || []) as { user_id: string; full_name: string | null }[]) {
      if (p.full_name) nomes.set(p.user_id, p.full_name);
    }
  }
  const nomeDe = (userId: string) =>
    nomes.get(userId) ?? (userId === order.user_id || userId === contexto.ownerId ? "Empresa (Admin)" : "Usuário");

  const resumo = resumirProducao(eventos, expediente, Date.now(), nomeDe);
  const fichaPorItem = new Map(fichasResult.fichas.map((f) => [f.itemId, f]));
  const resumoPorItem = new Map(resumo.itens.map((i) => [i.orderItemId, i]));
  const perdas = perdasRows.map((row) => perdaDaLinha(row, nomeDe));

  const itens = items.map((item) => {
    const ficha = fichaPorItem.get(item.id);
    const apontado = resumoPorItem.get(item.id);
    return {
      id: item.id,
      title: item.title,
      minutos_previstos: ficha?.ficha.minutosTotais ?? 0,
      aberto: apontado?.aberto ?? null,
      feitos: apontado?.feitos ?? [],
      minutos_reais: apontado?.minutosReais ?? 0,
      insumos: (ficha?.ficha.insumos ?? []).map((i) => ({
        materialId: i.materialId,
        nome: i.nome,
        unidade: i.unidade,
        quantidadePrevista: i.quantidade,
        quantidadeLiquida: i.quantidadeLiquida,
        natureza: i.natureza,
        chave: i.chave,
        ajustavel: i.ajustavel,
        rendimento: i.rendimento,
        rendimentoDoCadastro: i.rendimentoDoCadastro,
        rendimentoEstimado: i.rendimentoEstimado,
        sobra: i.sobra,
        rodagem: i.rodagem,
        corte: i.corte,
        larguraBobinaM: i.larguraBobinaM,
      })),
      perdas: perdas.filter((p) => p.orderItemId === item.id),
      plano: planoValido(item.production_plan),
    };
  });

  const painel: PainelDeProducao = {
    ok: true,
    expediente,
    order: {
      id: order.id,
      number: order.number,
      status: order.status,
      production_started_at: order.production_started_at,
      production_accumulated_seconds: Number(order.production_accumulated_seconds || 0),
      production_last_stopped_at: order.production_last_stopped_at,
      production_first_started_at: order.production_first_started_at,
      production_finished_at: order.production_finished_at,
      minutos_previstos: itens.reduce((acc, i) => acc + i.minutos_previstos, 0),
    },
    resumo,
    itens,
    perdas,
    podeAprovarPerdas: contexto.pode("materials.write"),
    perdasExigemAprovacaoAcimaPct,
  };
  return { painel };
}
