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

/**
 * Custo fixo mensal da empresa.
 *
 * O consolidado vem do banco, que soma duas fontes: a média do que foi
 * realmente pago em categorias classificadas como fixas, e o que a pessoa
 * declarou à mão para o que ela paga e não lança aqui.
 *
 * O que ainda não foi classificado viaja junto na resposta e é mostrado na
 * tela. Um custo fixo incompleto produz ponto de equilíbrio otimista, que é
 * pior do que não ter ponto de equilíbrio nenhum — a empresa acha que já passou
 * do ponto e para de correr atrás.
 */

const COMPORTAMENTOS = ["fixo", "variavel", "nao_operacional", "fixo_producao"] as const;

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

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

  const meses = Number(new URL(req.url).searchParams.get("meses") ?? 3) || 3;
  const admin = createAdminClient();

  const inicio = new Date();
  inicio.setDate(1);
  inicio.setMonth(inicio.getMonth() - meses);
  const fim = new Date();
  fim.setDate(1);

  const [consolidado, categorias, declarados, lancadas, config] = await Promise.all([
    admin.rpc("custo_fixo_mensal", { p_company_id: ctx.companyId, p_meses: meses }),
    admin
      .from("financial_categories")
      .select("id,name,cost_behavior")
      .eq("company_id", ctx.companyId)
      .order("name"),
    admin
      .from("company_fixed_costs")
      .select("id,name,monthly_amount,active,notes")
      .eq("company_id", ctx.companyId)
      .order("name"),
    /* As contas que formam a media, para abri-la por categoria: um agregado
       sozinho e um numero que ninguem confere. */
    admin
      .from("accounts_payable")
      .select("amount,category_id,financial_categories!inner(name,cost_behavior)")
      .eq("company_id", ctx.companyId)
      .is("reversed_at", null)
      .gte("due_date", inicio.toISOString().slice(0, 10))
      .lt("due_date", fim.toISOString().slice(0, 10)),
    admin
      .from("company_pricing_settings")
      .select("productive_hours_month")
      .eq("company_id", ctx.companyId)
      .maybeSingle(),
  ]);

  /* Media mensal por categoria fixa — as de producao tambem, marcadas: elas
     somam no total do equilibrio, so ficam fora da hora da estrutura. */
  const porCategoria = new Map<string, { total: number; producao: boolean }>();
  for (const linha of (lancadas.data ?? []) as unknown as {
    amount: number | null;
    financial_categories: { name: string; cost_behavior: string | null } | { name: string; cost_behavior: string | null }[];
  }[]) {
    const cat = Array.isArray(linha.financial_categories) ? linha.financial_categories[0] : linha.financial_categories;
    if (cat?.cost_behavior !== "fixo" && cat?.cost_behavior !== "fixo_producao") continue;
    const atual = porCategoria.get(cat.name) ?? { total: 0, producao: cat.cost_behavior === "fixo_producao" };
    atual.total += Number(linha.amount) || 0;
    porCategoria.set(cat.name, atual);
  }
  const mediaPorCategoria = [...porCategoria.entries()]
    .map(([nome, c]) => ({
      nome,
      media: Math.round((c.total / Math.max(meses, 1)) * 100) / 100,
      producao: c.producao,
    }))
    .sort((a, b) => b.media - a.media);

  const dadosFixos = consolidado.data as { total_mensal?: number; producao_mensal?: number } | null;
  const fixoMensal = Number(dadosFixos?.total_mensal ?? 0);
  const producaoMensal = Number(dadosFixos?.producao_mensal ?? 0);
  const estruturaMensal = Math.max(0, Math.round((fixoMensal - producaoMensal) * 100) / 100);
  const horasMes = Number(config.data?.productive_hours_month ?? 0);

  return NextResponse.json({
    ok: true,
    consolidado: consolidado.data ?? null,
    categorias: categorias.data ?? [],
    declarados: declarados.data ?? [],
    mediaPorCategoria,
    /* A hora da estrutura: o que cada hora produtiva precisa cobrir de custo
       fixo. E ela que a precificacao multiplica pelo tempo da ficha. */
    horaDaEstrutura: {
      configurada: horasMes > 0,
      horasMes,
      estruturaMensal,
      producaoMensal,
      taxa: horasMes > 0 ? Math.round((estruturaMensal / horasMes) * 100) / 100 : 0,
    },
  });
}

/** Classifica uma categoria como fixa, variável ou não operacional. */
export async function PATCH(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

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

  const body = (await req.json().catch(() => null)) as
    | { category_id?: unknown; cost_behavior?: unknown }
    | null;

  /* As horas produtivas da empresa: o divisor da hora da estrutura, gravado
     junto das demais políticas de precificação. Nulo desliga a absorção. */
  if (body && "productive_hours_month" in (body as object)) {
    const valor = Number((body as { productive_hours_month?: unknown }).productive_hours_month);
    const admin = createAdminClient();
    const { error } = await admin
      .from("company_pricing_settings")
      .upsert(
        {
          company_id: ctx.companyId,
          productive_hours_month: Number.isFinite(valor) && valor > 0 ? valor : null,
        },
        { onConflict: "company_id" }
      );
    if (error) return NextResponse.json({ error: error.message }, { status: 400 });
    return NextResponse.json({ ok: true });
  }

  const id = typeof body?.category_id === "string" ? body.category_id : "";
  if (!id) return NextResponse.json({ error: "Categoria inválida." }, { status: 400 });

  const comportamento = body?.cost_behavior === null ? null : String(body?.cost_behavior ?? "");
  if (comportamento !== null && !COMPORTAMENTOS.includes(comportamento as (typeof COMPORTAMENTOS)[number])) {
    return NextResponse.json({ error: "Classificação inválida." }, { status: 400 });
  }

  const admin = createAdminClient();
  const { error } = await admin
    .from("financial_categories")
    .update({ cost_behavior: comportamento })
    .eq("id", id)
    /* Amarra na empresa do usuario: sem isto, um id de outra empresa passaria,
       porque a chave de servico ignora RLS. */
    .eq("company_id", ctx.companyId);

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });
  return NextResponse.json({ ok: true });
}

/** Cria ou atualiza um custo fixo declarado à mão. */
export async function POST(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

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

  const body = (await req.json().catch(() => null)) as
    | { id?: unknown; name?: unknown; monthly_amount?: unknown; active?: unknown }
    | null;

  const nome = String(body?.name ?? "").trim();
  if (!nome) return NextResponse.json({ error: "Informe o nome do custo." }, { status: 400 });

  const valor = Number(body?.monthly_amount);
  if (!Number.isFinite(valor) || valor < 0) {
    return NextResponse.json({ error: "Valor mensal inválido." }, { status: 400 });
  }

  const admin = createAdminClient();
  const registro = {
    company_id: ctx.companyId,
    name: nome,
    monthly_amount: Math.round(valor * 100) / 100,
    active: body?.active === false ? false : true,
    updated_at: new Date().toISOString(),
  };

  const id = typeof body?.id === "string" ? body.id : "";
  const { error } = id
    ? await admin.from("company_fixed_costs").update(registro).eq("id", id).eq("company_id", ctx.companyId)
    : await admin.from("company_fixed_costs").insert(registro);

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });
  return NextResponse.json({ ok: true });
}

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

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

  const id = new URL(req.url).searchParams.get("id") ?? "";
  if (!id) return NextResponse.json({ error: "Identificador ausente." }, { status: 400 });

  const admin = createAdminClient();
  const { error } = await admin
    .from("company_fixed_costs")
    .delete()
    .eq("id", id)
    .eq("company_id", ctx.companyId);

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });
  return NextResponse.json({ ok: true });
}
