import { NextResponse } from "next/server";
import { createClient } from "@/lib/supabase-server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { avisarLoja } from "@/lib/loja/avisar-loja";
import {
  lerCusto,
  lerGrupos,
  lerServicos,
  type CustoDoServico,
  type GrupoDeServicos,
  type ServicoOpcional,
} from "@/lib/servicos-opcionais";

/**
 * A biblioteca de serviços opcionais da empresa.
 *
 * Um cadastro só para todos os produtos: "Criação de arte" tem um preço, não
 * mil. O produto apenas liga ou desliga o que a biblioteca oferece.
 *
 * GET devolve grupos, serviços e o custo de cada um. PUT recebe a lista
 * inteira e sincroniza — quem sumiu da lista é **desativado, nunca apagado**,
 * porque o id do serviço é o `option_id` que vive dentro de pedidos já
 * fechados e das receitas que o banco lê para congelar a produção.
 */

type Sessao = { supabase: Awaited<ReturnType<typeof createClient>>; userId: string; empresa: string };

async function entrar(permissao: "products.read" | "products.catalog.write") {
  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 empresa = ctx.ownerId ?? ctx.companyId;
  if (!empresa) return { erro: NextResponse.json({ error: "Empresa não identificada." }, { status: 400 }) };
  return { sessao: { supabase, userId: user.id, empresa } as Sessao };
}

/* Tabela que ainda não existe não pode derrubar a tela do produto: sem a
   migração 113, a biblioteca aparece vazia e o resto continua. */
function relacaoAusente(mensagem: unknown, tabela: string) {
  const texto = String((mensagem as { message?: unknown })?.message ?? mensagem ?? "").toLowerCase();
  return texto.includes(tabela) && texto.includes("does not exist");
}

export async function GET() {
  const { erro, sessao } = await entrar("products.read");
  if (erro || !sessao) return erro!;
  const { supabase, empresa } = sessao;

  const [gruposRes, servicosRes, custosRes] = await Promise.all([
    supabase.from("service_groups").select("*").eq("user_id", empresa).order("position"),
    supabase.from("optional_services").select("*").eq("user_id", empresa).order("position"),
    supabase.from("optional_service_costs").select("*").eq("user_id", empresa),
  ]);

  if (gruposRes.error && !relacaoAusente(gruposRes.error, "service_groups")) {
    return NextResponse.json({ error: gruposRes.error.message }, { status: 400 });
  }
  if (servicosRes.error && !relacaoAusente(servicosRes.error, "optional_services")) {
    return NextResponse.json({ error: servicosRes.error.message }, { status: 400 });
  }

  const custos: Record<string, CustoDoServico> = {};
  for (const linha of (custosRes.data as Array<Record<string, unknown>> | null) || []) {
    const id = String(linha.service_id ?? "");
    if (id) custos[id] = lerCusto(linha);
  }

  return NextResponse.json({
    grupos: lerGrupos(gruposRes.data),
    servicos: lerServicos(servicosRes.data),
    custos,
  });
}

export async function PUT(req: Request) {
  const { erro, sessao } = await entrar("products.catalog.write");
  if (erro || !sessao) return erro!;
  const { supabase, empresa } = sessao;

  const corpo = (await req.json().catch(() => null)) as {
    grupos?: unknown;
    servicos?: unknown;
    custos?: unknown;
  } | null;

  const grupos = lerGrupos(corpo?.grupos);
  const servicos = lerServicos(corpo?.servicos);
  const custosBrutos = (corpo?.custos ?? {}) as Record<string, unknown>;
  const agora = new Date().toISOString();

  /* Grupo sem nome não vira seção na loja: some da lista e é desativado. */
  const gruposValidos = grupos.filter((g: GrupoDeServicos) => g.nome.trim().length > 0);

  if (gruposValidos.length > 0) {
    const { error } = await supabase.from("service_groups").upsert(
      gruposValidos.map((g, indice) => ({
        id: g.id,
        user_id: empresa,
        name: g.nome.trim(),
        selection_mode: g.modo,
        /* Obrigatório só existe em grupo de escolha única: exigir uma resposta
           num grupo de marcar vários travaria a compra sem saída. */
        required: g.modo === "uma" ? g.obrigatorio : false,
        position: indice,
        active: true,
        updated_at: agora,
      })),
      { onConflict: "id" }
    );
    if (error) {
      if (relacaoAusente(error, "service_groups")) {
        return NextResponse.json({ error: "Banco sem a migração 113 dos serviços." }, { status: 400 });
      }
      return NextResponse.json({ error: error.message }, { status: 400 });
    }
  }

  const idsDeGrupo = new Set(gruposValidos.map((g) => g.id));
  const servicosValidos = servicos.filter((s: ServicoOpcional) => s.titulo.trim().length > 0);

  if (servicosValidos.length > 0) {
    const { error } = await supabase.from("optional_services").upsert(
      servicosValidos.map((s, indice) => ({
        id: s.id,
        user_id: empresa,
        group_id: s.grupoId && idsDeGrupo.has(s.grupoId) ? s.grupoId : null,
        title: s.titulo.trim(),
        description: s.descricao?.trim() || null,
        details: s.detalhes?.trim() || null,
        icon: s.icone,
        price: s.preco,
        charge_per_unit: s.porPeca,
        apply_to_all: s.valeParaTodos,
        position: indice,
        active: s.ativo,
        updated_at: agora,
      })),
      { onConflict: "id" }
    );
    if (error) return NextResponse.json({ error: error.message }, { status: 400 });

    const linhasDeCusto = servicosValidos.map((s) => {
      const custo = lerCusto(custosBrutos[s.id]);
      const usaTempo = custo.tipo === "mao_de_obra" || custo.tipo === "maquina";
      return {
        service_id: s.id,
        user_id: empresa,
        cost_kind: custo.tipo,
        /* Cada tipo de custo guarda só o que usa: sobra de um cadastro
           anterior viraria custo fantasma na próxima venda. */
        material_id: custo.tipo === "insumo" ? custo.materialId : null,
        material_qty: custo.tipo === "insumo" ? custo.materialQtd : 0,
        role_id: custo.tipo === "mao_de_obra" ? custo.roleId : null,
        machine_id: custo.tipo === "maquina" ? custo.machineId : null,
        minutes: usaTempo ? custo.minutos : 0,
        setup_minutes: usaTempo ? custo.setupMinutos : 0,
        updated_at: agora,
      };
    });
    const { error: custoError } = await supabase
      .from("optional_service_costs")
      .upsert(linhasDeCusto, { onConflict: "service_id" });
    if (custoError) return NextResponse.json({ error: custoError.message }, { status: 400 });
  }

  /* O que saiu da lista é desativado. Apagar levaria junto o vínculo com
     pedidos antigos, e a ficha de produção deles pararia de explicar de onde
     veio aquele custo. */
  const idsVivos = new Set(servicosValidos.map((s) => s.id));
  const { data: existentes } = await supabase
    .from("optional_services")
    .select("id,active")
    .eq("user_id", empresa);
  const paraDesativar = ((existentes as Array<{ id: string; active: boolean }> | null) || []).filter(
    (linha) => linha.active !== false && !idsVivos.has(linha.id)
  );
  for (const linha of paraDesativar) {
    await supabase
      .from("optional_services")
      .update({ active: false, updated_at: agora })
      .eq("id", linha.id)
      .eq("user_id", empresa);
  }

  const { data: gruposExistentes } = await supabase
    .from("service_groups")
    .select("id,active")
    .eq("user_id", empresa);
  const gruposParaDesativar = ((gruposExistentes as Array<{ id: string; active: boolean }> | null) || []).filter(
    (linha) => linha.active !== false && !idsDeGrupo.has(linha.id)
  );
  for (const linha of gruposParaDesativar) {
    await supabase
      .from("service_groups")
      .update({ active: false, updated_at: agora })
      .eq("id", linha.id)
      .eq("user_id", empresa);
  }

  /* A página do produto guarda os serviços por dois minutos. */
  avisarLoja(empresa, req);
  return NextResponse.json({ ok: true });
}
