import { prisma } from "@/lib/prisma";
import { requirePermission, withGuard } from "@/lib/guard";
import { ok } from "@/lib/http";
import { progressPct } from "@/lib/utils";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

type Fin = { budget: number; adSpend: number; revenue: number; leads: number; trials: number; enrollments: number };

const ZERO: Fin = { budget: 0, adSpend: 0, revenue: 0, leads: 0, trials: 0, enrollments: 0 };

// Extract ONLY the numeric fields — a raw Prisma financials row also carries
// id/companyId/year/month, which must not leak into metrics() and clobber the
// company id when spread.
function pick(f: Fin): Fin {
  return {
    budget: f.budget,
    adSpend: f.adSpend,
    revenue: f.revenue,
    leads: f.leads,
    trials: f.trials,
    enrollments: f.enrollments,
  };
}

function add(a: Fin, b: Fin): Fin {
  return {
    budget: a.budget + b.budget,
    adSpend: a.adSpend + b.adSpend,
    revenue: a.revenue + b.revenue,
    leads: a.leads + b.leads,
    trials: a.trials + b.trials,
    enrollments: a.enrollments + b.enrollments,
  };
}

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

// Derive every management metric from raw monthly financials.
function metrics(f: Fin) {
  return {
    ...f,
    roas: f.adSpend > 0 ? r2(f.revenue / f.adSpend) : null,
    roi: f.adSpend > 0 ? Math.round(((f.revenue - f.adSpend) / f.adSpend) * 100) : null,
    cac: f.enrollments > 0 ? r2(f.adSpend / f.enrollments) : null,
    cpl: f.leads > 0 ? r2(f.adSpend / f.leads) : null,
    leadToTrial: f.leads > 0 ? Math.round((f.trials / f.leads) * 100) : null,
    trialToEnroll: f.trials > 0 ? Math.round((f.enrollments / f.trials) * 100) : null,
    leadToEnroll: f.leads > 0 ? Math.round((f.enrollments / f.leads) * 100) : null,
    budgetVariancePct: f.budget > 0 ? Math.round(((f.adSpend - f.budget) / f.budget) * 100) : null,
    overBudget: f.budget > 0 && f.adSpend > f.budget,
    budgetUsedPct: f.budget > 0 ? Math.round((f.adSpend / f.budget) * 100) : null,
  };
}

// % change of a metric vs the previous month (null when not comparable).
function pctChange(cur: number | null, prev: number | null): number | null {
  if (cur === null || prev === null || prev === 0) return null;
  return Math.round(((cur - prev) / prev) * 100);
}

// GET /api/dashboard/executive?year=&month= — management scorecard (Manager-tier).
export const GET = withGuard(async (req: Request) => {
  await requirePermission("dashboard:viewTeam");
  const url = new URL(req.url);
  const now = new Date();
  const year = Number(url.searchParams.get("year")) || now.getFullYear();
  const month = Number(url.searchParams.get("month")) || now.getMonth() + 1;
  const prevPeriod = month === 1 ? { y: year - 1, m: 12 } : { y: year, m: month - 1 };

  const [companies, kpis] = await Promise.all([
    prisma.company.findMany({
      where: { active: true },
      include: {
        financials: {
          where: {
            OR: [
              { year, month },
              { year: prevPeriod.y, month: prevPeriod.m },
            ],
          },
        },
      },
      orderBy: { name: "asc" },
    }),
    prisma.kpiIndicator.findMany({
      include: { monthlyValues: { where: { year, month } } },
    }),
  ]);

  let curTotal = { ...ZERO };
  let prevTotal = { ...ZERO };
  const companyRows = companies.map((c) => {
    const cur = pick(c.financials.find((f) => f.year === year && f.month === month) ?? ZERO);
    const prev = pick(c.financials.find((f) => f.year === prevPeriod.y && f.month === prevPeriod.m) ?? ZERO);
    curTotal = add(curTotal, cur);
    prevTotal = add(prevTotal, prev);
    return { id: c.id, name: c.name, ...metrics(cur) };
  });

  const overall = metrics(curTotal);
  const prevOverall = metrics(prevTotal);

  // Month-over-month % change for the headline metrics.
  const deltas = {
    revenue: pctChange(overall.revenue, prevOverall.revenue),
    adSpend: pctChange(overall.adSpend, prevOverall.adSpend),
    enrollments: pctChange(overall.enrollments, prevOverall.enrollments),
    leads: pctChange(overall.leads, prevOverall.leads),
    roas: pctChange(overall.roas, prevOverall.roas),
    roi: pctChange(overall.roi, prevOverall.roi),
    cac: pctChange(overall.cac, prevOverall.cac),
    cpl: pctChange(overall.cpl, prevOverall.cpl),
    leadToEnroll: pctChange(overall.leadToEnroll, prevOverall.leadToEnroll),
  };

  // KPI attainment grouped by business-area category, for the selected month.
  const catMap = new Map<string, { sum: number; count: number }>();
  for (const k of kpis) {
    const mv = k.monthlyValues[0];
    const target = mv?.targetValue ?? k.targetValue;
    const actual = mv?.actualValue ?? 0;
    const pct = progressPct(actual, target);
    const cur = catMap.get(k.category) ?? { sum: 0, count: 0 };
    cur.sum += pct;
    cur.count += 1;
    catMap.set(k.category, cur);
  }
  const categories = [...catMap.entries()]
    .map(([category, v]) => ({ category, kpiCount: v.count, attainment: Math.round(v.sum / v.count) }))
    .sort((a, b) => b.attainment - a.attainment);

  return ok({
    year,
    month,
    overall,
    deltas,
    funnel: {
      leads: curTotal.leads,
      trials: curTotal.trials,
      enrollments: curTotal.enrollments,
      leadToTrial: overall.leadToTrial,
      trialToEnroll: overall.trialToEnroll,
      leadToEnroll: overall.leadToEnroll,
    },
    companies: companyRows,
    categories,
  });
});
