"use client";

import React, { useState, useMemo, useCallback } from "react";
import { trpc } from "~/lib/trpc/client.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
import { ProgressRing } from "~/components/ui/ProgressRing.js";
import { downloadWorkbookSheets } from "~/lib/workbook-export.js";

// ─── Helpers ─────────────────────────────────────────────────────────────────

function formatMonth(key: string): string {
  const [y, m] = key.split("-");
  const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  return `${months[Number(m) - 1]} ${y}`;
}

function pct(ratio: number): string {
  return `${Math.round(ratio * 100)}%`;
}

function chgColor(ratio: number, target: number): string {
  if (ratio >= target) return "text-green-700 dark:text-green-400";
  if (ratio >= target - 0.1) return "text-yellow-700 dark:text-yellow-400";
  return "text-red-700 dark:text-red-400";
}

function barStyle(ratio: number, color: string): React.CSSProperties {
  return {
    background: `linear-gradient(to right, ${color} ${Math.min(ratio * 100, 100)}%, transparent ${Math.min(ratio * 100, 100)}%)`,
  };
}

type GroupByField = "none" | "orgUnit" | "mgmtGroup" | "country";

interface ResourceRow {
  id: string;
  eid: string;
  displayName: string;
  fte: number;
  country: string | null;
  federalState: string | null;
  city: string | null;
  orgUnit: string | null;
  mgmtGroup: string | null;
  mgmtLevel: string | null;
  targetPct: number;
  months: MonthData[];
}

interface MonthData {
  monthKey: string;
  sah: number;
  sahHours?: number;
  chg?: number;
  bd?: number;
  mdi?: number;
  mo?: number;
  pdr?: number;
  absence?: number;
  unassigned?: number;
  chargeabilityRatio?: number;
  businessDevelopmentRatio?: number;
  marketDevelopmentInnovationRatio?: number;
  managementOverheadRatio?: number;
  peopleDevelopmentRecruitingRatio?: number;
  plannedAbsenceRatio?: number;
  unassignedRatio?: number;
  chargeabilityHours?: number;
  businessDevelopmentHours?: number;
  marketDevelopmentInnovationHours?: number;
  managementOverheadHours?: number;
  peopleDevelopmentRecruitingHours?: number;
  plannedAbsenceHours?: number;
  unassignedHours?: number;
  targetRatio?: number;
  targetHours?: number;
  gapRatio?: number;
  gapHours?: number;
  derivation?: {
    baseAvailableHours: number;
    publicHolidayCount: number;
    publicHolidayWorkdayCount: number;
    publicHolidayHoursDeduction: number;
    absenceDayEquivalent: number;
    absenceHoursDeduction: number;
    effectiveAvailableHours: number;
  };
}

function formatLocation(resource: Pick<ResourceRow, "country" | "federalState" | "city">): string {
  return [resource.country, resource.federalState, resource.city].filter(Boolean).join(" / ") || "No calendar context";
}

function monthChargeabilityRatio(month: MonthData): number {
  return month.chargeabilityRatio ?? month.chg ?? 0;
}

function monthBusinessDevelopmentRatio(month: MonthData): number {
  return month.businessDevelopmentRatio ?? month.bd ?? 0;
}

function monthMarketDevelopmentInnovationRatio(month: MonthData): number {
  return month.marketDevelopmentInnovationRatio ?? month.mdi ?? 0;
}

function monthManagementOverheadRatio(month: MonthData): number {
  return month.managementOverheadRatio ?? month.mo ?? 0;
}

function monthPeopleDevelopmentRecruitingRatio(month: MonthData): number {
  return month.peopleDevelopmentRecruitingRatio ?? month.pdr ?? 0;
}

function monthPlannedAbsenceRatio(month: MonthData): number {
  return month.plannedAbsenceRatio ?? month.absence ?? 0;
}

function monthUnassignedRatio(month: MonthData): number {
  return month.unassignedRatio ?? month.unassigned ?? 0;
}

function monthSahHours(month: MonthData): number {
  return month.sahHours ?? month.sah;
}

function monthTargetRatio(month: MonthData, resourceTargetRatio: number): number {
  return month.targetRatio ?? resourceTargetRatio;
}

function monthGapRatio(month: MonthData, resourceTargetRatio: number): number {
  return month.gapRatio ?? (monthChargeabilityRatio(month) - monthTargetRatio(month, resourceTargetRatio));
}

interface GroupSummary {
  label: string;
  resources: ResourceRow[];
  monthTotals: { monthKey: string; chg: number; target: number; gap: number; totalFte: number }[];
}

interface ReportExplainability {
  locationFields: string[];
  monthDerivationFields: string[];
  activeFilters: string[];
  formulas: {
    sah: string;
    chargeabilityPct: string;
    targetHours: string;
    gapHours: string;
  };
  notes: string[];
}

interface ReportFilterSummary {
  startMonth: string;
  endMonth: string;
  includeProposed: boolean;
  groupBy: GroupByField;
}

function computeGroupMonthTotals(
  resources: ResourceRow[],
  monthKeys: string[],
): GroupSummary["monthTotals"] {
  const totalFte = resources.reduce((sum, r) => sum + r.fte, 0);
  return monthKeys.map((key, idx) => {
    if (totalFte === 0) return { monthKey: key, chg: 0, target: 0, gap: 0, totalFte: 0 };
    const chg = resources.reduce((sum, r) => sum + r.fte * monthChargeabilityRatio(r.months[idx]!), 0) / totalFte;
    const target = resources.reduce((sum, r) => sum + r.fte * monthTargetRatio(r.months[idx]!, r.targetPct), 0) / totalFte;
    return { monthKey: key, chg, target, gap: chg - target, totalFte };
  });
}

// ─── Export ──────────────────────────────────────────────────────────────────

async function exportToExcel(
  resources: ResourceRow[],
  monthKeys: string[],
  groupTotals: { monthKey: string; chg: number; target: number; gap: number; totalFte: number }[],
  groups: GroupSummary[],
  groupBy: GroupByField,
  explainability: ReportExplainability | undefined,
  filterSummary: ReportFilterSummary,
) {
  const headers = ["Name", "EID", "FTE", "Target", "Country", "State", "City", "Org Unit", "Mgmt Group", "Mgmt Level"];
  for (const key of monthKeys) {
    headers.push(`Chg % ${formatMonth(key)}`);
    headers.push(`Chg H ${formatMonth(key)}`);
    headers.push(`Target % ${formatMonth(key)}`);
    headers.push(`Target H ${formatMonth(key)}`);
    headers.push(`Gap % ${formatMonth(key)}`);
    headers.push(`Gap H ${formatMonth(key)}`);
    headers.push(`BD % ${formatMonth(key)}`);
    headers.push(`MD&I % ${formatMonth(key)}`);
    headers.push(`M&O % ${formatMonth(key)}`);
    headers.push(`PD&R % ${formatMonth(key)}`);
    headers.push(`Planned Abs % ${formatMonth(key)}`);
    headers.push(`Free % ${formatMonth(key)}`);
    headers.push(`Base ${formatMonth(key)}`);
    headers.push(`Hol ${formatMonth(key)}`);
    headers.push(`AbsH ${formatMonth(key)}`);
    headers.push(`SAH ${formatMonth(key)}`);
  }

  const rows: (string | number)[][] = [headers];

  // Group total row
  const totalRow: (string | number)[] = [
    "GROUP TOTAL", "", groupTotals[0]?.totalFte ?? 0, groupTotals[0]?.target ?? 0,
    "", "", "", "", "", "",
  ];
  for (const gt of groupTotals) {
    totalRow.push(
      Math.round(gt.chg * 1000) / 1000,
      0,
      Math.round(gt.target * 1000) / 1000,
      0,
      Math.round(gt.gap * 1000) / 1000,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
    );
  }
  rows.push(totalRow);

  // Sub-group totals if grouping
  if (groupBy !== "none") {
    for (const g of groups) {
      const subRow: (string | number)[] = [
        `  ${g.label} (${g.resources.length})`, "", g.monthTotals[0]?.totalFte ?? 0,
        g.monthTotals[0]?.target ?? 0, "", "", "", "", "", "",
      ];
      for (const mt of g.monthTotals) {
        subRow.push(
          Math.round(mt.chg * 1000) / 1000,
          0,
          Math.round(mt.target * 1000) / 1000,
          0,
          Math.round(mt.gap * 1000) / 1000,
          0,
          0,
          0,
          0,
          0,
          0,
          0,
          0,
          0,
          0,
          0,
        );
      }
      rows.push(subRow);
    }
    rows.push([]); // blank separator
  }

  // Resource rows
  for (const r of resources) {
    const row: (string | number)[] = [
      r.displayName, r.eid, r.fte, Math.round(r.targetPct * 100) / 100,
      r.country ?? "", r.federalState ?? "", r.city ?? "", r.orgUnit ?? "", r.mgmtGroup ?? "", r.mgmtLevel ?? "",
    ];
    for (const m of r.months) {
      const chargeabilityRatio = monthChargeabilityRatio(m);
      const businessDevelopmentRatio = monthBusinessDevelopmentRatio(m);
      const marketDevelopmentInnovationRatio = monthMarketDevelopmentInnovationRatio(m);
      const managementOverheadRatio = monthManagementOverheadRatio(m);
      const peopleDevelopmentRecruitingRatio = monthPeopleDevelopmentRecruitingRatio(m);
      const plannedAbsenceRatio = monthPlannedAbsenceRatio(m);
      const unassignedRatio = monthUnassignedRatio(m);
      const targetRatio = monthTargetRatio(m, r.targetPct);
      const gapRatio = monthGapRatio(m, r.targetPct);

      row.push(
        Math.round(chargeabilityRatio * 1000) / 1000,
        Math.round((m.chargeabilityHours ?? (monthSahHours(m) * chargeabilityRatio)) * 10) / 10,
        Math.round(targetRatio * 1000) / 1000,
        Math.round((m.targetHours ?? (monthSahHours(m) * targetRatio)) * 10) / 10,
        Math.round(gapRatio * 1000) / 1000,
        Math.round((m.gapHours ?? (monthSahHours(m) * gapRatio)) * 10) / 10,
        Math.round(businessDevelopmentRatio * 1000) / 1000,
        Math.round(marketDevelopmentInnovationRatio * 1000) / 1000,
        Math.round(managementOverheadRatio * 1000) / 1000,
        Math.round(peopleDevelopmentRecruitingRatio * 1000) / 1000,
        Math.round(plannedAbsenceRatio * 1000) / 1000,
        Math.round(unassignedRatio * 1000) / 1000,
        Math.round((m.derivation?.baseAvailableHours ?? 0) * 10) / 10,
        Math.round((m.derivation?.publicHolidayHoursDeduction ?? 0) * 10) / 10,
        Math.round((m.derivation?.absenceHoursDeduction ?? 0) * 10) / 10,
        Math.round(monthSahHours(m) * 10) / 10,
      );
    }
    rows.push(row);
  }

  const explainabilityRows: (string | number)[][] = [
    ["Chargeability Forecast Explainability"],
    [],
    ["Report range", `${formatMonth(filterSummary.startMonth)} to ${formatMonth(filterSummary.endMonth)}`],
    ["Include proposed work", filterSummary.includeProposed ? "Yes" : "No"],
    ["Grouping", filterSummary.groupBy === "none" ? "Flat resource view" : filterSummary.groupBy],
    [],
    ["Location basis fields", ...(explainability?.locationFields ?? [])],
    ["Month derivation fields", ...(explainability?.monthDerivationFields ?? [])],
    ["Active filters", ...((explainability?.activeFilters.length ?? 0) > 0 ? explainability!.activeFilters : ["none"])],
    [],
    ["Formula", "Definition"],
    ["SAH", explainability?.formulas.sah ?? ""],
    ["Chargeability %", explainability?.formulas.chargeabilityPct ?? ""],
    ["Target hours", explainability?.formulas.targetHours ?? ""],
    ["Gap hours", explainability?.formulas.gapHours ?? ""],
    [],
    ["Notes"],
    ...(explainability?.notes.map((note) => [note]) ?? []),
    [],
    ["Export coverage"],
    ["Resource rows already include Country, State, City, Org Unit, Mgmt Group, Mgmt Level."],
    ["Each month exports Base, Holiday, Absence, and SAH hours alongside chargeability, target, and gap."],
  ];

  await downloadWorkbookSheets("chargeability-report.xlsx", [
    { name: "Chargeability", rows },
    { name: "Explainability", rows: explainabilityRows },
  ]);
}

function exportToCsv(
  resources: ResourceRow[],
  monthKeys: string[],
) {
  const headers = ["Name", "EID", "FTE", "Target", "Country", "State", "City", "Org Unit", "Mgmt Group", "Mgmt Level"];
  for (const key of monthKeys) {
    headers.push(
      `ChgPct_${key}`,
      `ChgHours_${key}`,
      `TargetPct_${key}`,
      `TargetHours_${key}`,
      `GapPct_${key}`,
      `GapHours_${key}`,
      `BDPct_${key}`,
      `MDIPct_${key}`,
      `MOPct_${key}`,
      `PDRPct_${key}`,
      `PlannedAbsPct_${key}`,
      `FreePct_${key}`,
      `BaseH_${key}`,
      `HolidayH_${key}`,
      `AbsenceH_${key}`,
      `SAH_${key}`,
    );
  }

  const lines = [headers.join(",")];
  for (const r of resources) {
    const vals = [
      `"${r.displayName}"`, r.eid, r.fte, r.targetPct,
      r.country ?? "", r.federalState ?? "", r.city ?? "", r.orgUnit ?? "", r.mgmtGroup ?? "", r.mgmtLevel ?? "",
    ];
    for (const m of r.months) {
      const chargeabilityRatio = monthChargeabilityRatio(m);
      const targetRatio = monthTargetRatio(m, r.targetPct);
      const gapRatio = monthGapRatio(m, r.targetPct);
      vals.push(
        chargeabilityRatio as unknown as string,
        (m.chargeabilityHours ?? (monthSahHours(m) * chargeabilityRatio)) as unknown as string,
        targetRatio as unknown as string,
        (m.targetHours ?? (monthSahHours(m) * targetRatio)) as unknown as string,
        gapRatio as unknown as string,
        (m.gapHours ?? (monthSahHours(m) * gapRatio)) as unknown as string,
        monthBusinessDevelopmentRatio(m) as unknown as string,
        monthMarketDevelopmentInnovationRatio(m) as unknown as string,
        monthManagementOverheadRatio(m) as unknown as string,
        monthPeopleDevelopmentRecruitingRatio(m) as unknown as string,
        monthPlannedAbsenceRatio(m) as unknown as string,
        monthUnassignedRatio(m) as unknown as string,
        (m.derivation?.baseAvailableHours ?? 0) as unknown as string,
        (m.derivation?.publicHolidayHoursDeduction ?? 0) as unknown as string,
        (m.derivation?.absenceHoursDeduction ?? 0) as unknown as string,
        monthSahHours(m) as unknown as string,
      );
    }
    lines.push(vals.join(","));
  }

  const blob = new Blob([lines.join("\n")], { type: "text/csv" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "chargeability-report.csv";
  a.click();
  URL.revokeObjectURL(url);
}

function toTitleLabel(value: string): string {
  return value
    .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
    .replace(/Id$/g, "")
    .replace(/^mdi$/i, "MD&I")
    .replace(/^pdr$/i, "PD&R")
    .replace(/^mgmt$/i, "Mgmt")
    .replace(/^orgUnit$/i, "Org Unit")
    .replace(/^federalState$/i, "Federal State")
    .replace(/^country$/i, "Country")
    .replace(/^city$/i, "City");
}

// ─── Component ───────────────────────────────────────────────────────────────

const NOW = new Date();
const DEFAULT_START = `${NOW.getFullYear()}-${String(NOW.getMonth() + 1).padStart(2, "0")}`;
const DEFAULT_END_DATE = new Date(NOW.getFullYear(), NOW.getMonth() + 5, 1);
const DEFAULT_END = `${DEFAULT_END_DATE.getFullYear()}-${String(DEFAULT_END_DATE.getMonth() + 1).padStart(2, "0")}`;

export function ChargeabilityReportClient() {
  const [startMonth, setStartMonth] = useState(DEFAULT_START);
  const [endMonth, setEndMonth] = useState(DEFAULT_END);
  const [orgUnitId, setOrgUnitId] = useState<string>("");
  const [mgmtGroupId, setMgmtGroupId] = useState<string>("");
  const [countryId, setCountryId] = useState<string>("");
  const [includeProposed, setIncludeProposed] = useState(false);
  const [nameSearch, setNameSearch] = useState("");
  const [groupBy, setGroupBy] = useState<GroupByField>("none");
  const [expandedResource, setExpandedResource] = useState<string | null>(null);
  const [expandedGroups, setExpandedGroups] = useState<Set<string>>(new Set());

  // Filter dropdowns data
  const orgUnitsQuery = trpc.orgUnit.list.useQuery();
  const mgmtGroupsQuery = trpc.managementLevel.listGroups.useQuery();
  const countriesQuery = trpc.country.list.useQuery();

  const reportQuery = trpc.chargeabilityReport.getReport.useQuery(
    {
      startMonth,
      endMonth,
      ...(orgUnitId ? { orgUnitId } : {}),
      ...(mgmtGroupId ? { managementLevelGroupId: mgmtGroupId } : {}),
      ...(countryId ? { countryId } : {}),
      includeProposed,
    },
    { placeholderData: (prev) => prev },
  );

  const data = reportQuery.data;

  const orgUnits = useMemo(() => {
    const items = orgUnitsQuery.data ?? [];
    return items.filter((u: { level: number }) => u.level === 7);
  }, [orgUnitsQuery.data]);

  const filteredResources = useMemo(() => {
    if (!data) return [];
    const query = nameSearch.trim().toLowerCase();
    if (!query) return data.resources;

    return data.resources.filter((resource) =>
      resource.displayName.toLowerCase().includes(query) ||
      resource.eid.toLowerCase().includes(query),
    );
  }, [data, nameSearch]);

  const filteredGroupTotals = useMemo(() => {
    if (!data) return [];
    return computeGroupMonthTotals(filteredResources, data.monthKeys);
  }, [data, filteredResources]);

  const averageTarget = filteredGroupTotals[0]?.target ?? 0;
  const averageChargeability = filteredGroupTotals[0]?.chg ?? 0;
  const averageGap = filteredGroupTotals[0]?.gap ?? 0;

  // Group resources by selected dimension
  const groups = useMemo((): GroupSummary[] => {
    if (!data || groupBy === "none") return [];

    const buckets = new Map<string, ResourceRow[]>();
    for (const r of filteredResources) {
      let key: string;
      switch (groupBy) {
        case "orgUnit": key = r.orgUnit ?? "(No Org Unit)"; break;
        case "mgmtGroup": key = r.mgmtGroup ?? "(No Mgmt Group)"; break;
        case "country": key = r.country ?? "(No Country)"; break;
      }
      const list = buckets.get(key) ?? [];
      list.push(r);
      buckets.set(key, list);
    }

    return Array.from(buckets.entries())
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([label, resources]) => ({
        label,
        resources,
        monthTotals: computeGroupMonthTotals(resources, data.monthKeys),
      }));
  }, [data, filteredResources, groupBy]);

  const toggleGroup = useCallback((label: string) => {
    setExpandedGroups((prev) => {
      const next = new Set(prev);
      if (next.has(label)) next.delete(label);
      else next.add(label);
      return next;
    });
  }, []);

  const handleExportExcel = useCallback(() => {
    if (!data) return;
    exportToExcel(
      filteredResources,
      data.monthKeys,
      filteredGroupTotals,
      groups,
      groupBy,
      data.explainability,
      {
        startMonth,
        endMonth,
        includeProposed,
        groupBy,
      },
    );
  }, [data, endMonth, filteredGroupTotals, filteredResources, groupBy, groups, includeProposed, startMonth]);

  const handleExportCsv = useCallback(() => {
    if (!data) return;
    exportToCsv(filteredResources, data.monthKeys);
  }, [data, filteredResources]);

  // ─── Render helpers ──────────────────────────────────────────────────────

  function renderResourceRow(r: ResourceRow) {
    return (
      <tr
        key={r.id}
        className="cursor-pointer transition-colors hover:bg-gray-50/90 dark:hover:bg-gray-800/50"
        onClick={() => setExpandedResource(expandedResource === r.id ? null : r.id)}
      >
        <td className="sticky left-0 z-10 bg-white/95 px-4 py-3 backdrop-blur dark:bg-slate-950/95">
          <div className="font-semibold text-gray-900 dark:text-gray-100">{r.displayName}</div>
          <div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
            {[r.eid, formatLocation(r), r.orgUnit].filter(Boolean).join(" · ")}
          </div>
        </td>
        <td className="px-3 py-3 text-center font-medium text-gray-600 dark:text-gray-300">{r.fte.toFixed(2)}</td>
        <td className="px-3 py-3 text-center font-medium text-gray-600 dark:text-gray-300">{pct(r.targetPct)}</td>
        {r.months.map((m) => (
          <td key={m.monthKey} className="px-3 py-3 text-center">
            {(() => {
              const chargeabilityRatio = monthChargeabilityRatio(m);
              const targetRatio = monthTargetRatio(m, r.targetPct);

              return (
            <div
              className={`rounded-xl border border-transparent px-2 py-1 font-semibold ${chgColor(chargeabilityRatio, targetRatio)}`}
              style={barStyle(chargeabilityRatio, chargeabilityRatio >= targetRatio ? "rgba(34,197,94,0.15)" : "rgba(239,68,68,0.1)")}
            >
              {pct(chargeabilityRatio)}
            </div>
              );
            })()}
          </td>
        ))}
      </tr>
    );
  }

  function renderExpandedRow(r: ResourceRow) {
    if (expandedResource !== r.id) return null;
    return (
      <tr key={`${r.id}-detail`} className="bg-gray-50/80 dark:bg-slate-900/70">
        <td className="sticky left-0 z-10 bg-gray-50/95 px-4 py-3 backdrop-blur dark:bg-slate-900/95" colSpan={3}>
          <div className="space-y-1 text-xs text-gray-500 dark:text-gray-400">
            <div>Mgmt: {r.mgmtGroup ?? "—"} / {r.mgmtLevel ?? "—"}</div>
            <div>Calendar basis: {formatLocation(r)}</div>
            <div className="mt-2 grid grid-cols-7 gap-1 text-[10px] uppercase tracking-[0.16em] text-gray-400 dark:text-gray-500">
              <span className="font-medium inline-flex items-center gap-0.5">Chg<InfoTooltip content="Chargeability: % of available hours on chargeable work." /></span>
              <span className="font-medium inline-flex items-center gap-0.5">BD<InfoTooltip content="Business Development hours as % of available time." /></span>
              <span className="font-medium inline-flex items-center gap-0.5">MD&I<InfoTooltip content="Market Development & Innovation hours %." /></span>
              <span className="font-medium inline-flex items-center gap-0.5">M&O<InfoTooltip content="Management & Overhead hours %." /></span>
              <span className="font-medium inline-flex items-center gap-0.5">PD&R<InfoTooltip content="People Development & Recruiting hours %." /></span>
              <span className="font-medium inline-flex items-center gap-0.5">Abs<InfoTooltip content="Absence (vacation, sick) as % of working time." /></span>
              <span className="font-medium inline-flex items-center gap-0.5">Free<InfoTooltip content="Unassigned / free capacity as % of available hours." /></span>
            </div>
          </div>
        </td>
        {r.months.map((m) => (
          <td key={m.monthKey} className="px-3 py-3 text-center">
            <div className="grid grid-cols-1 gap-1 rounded-xl bg-white/70 px-2 py-2 text-[10px] text-gray-500 shadow-sm dark:bg-slate-950/40 dark:text-gray-400">
              <span className="font-semibold text-green-600 dark:text-green-400">{pct(monthChargeabilityRatio(m))}</span>
              <span>{pct(monthBusinessDevelopmentRatio(m))}</span>
              <span>{pct(monthMarketDevelopmentInnovationRatio(m))}</span>
              <span>{pct(monthManagementOverheadRatio(m))}</span>
              <span>{pct(monthPeopleDevelopmentRecruitingRatio(m))}</span>
              <span className="text-orange-500 dark:text-orange-300">{pct(monthPlannedAbsenceRatio(m))}</span>
              <span className="text-gray-400 dark:text-gray-500">{pct(monthUnassignedRatio(m))}</span>
              {m.derivation ? (
                <span className="pt-1 text-[9px] leading-4 text-gray-400 dark:text-gray-500">
                  {m.derivation.baseAvailableHours}h - {m.derivation.publicHolidayHoursDeduction}h hol - {m.derivation.absenceHoursDeduction}h abs = {m.derivation.effectiveAvailableHours}h SAH
                </span>
              ) : null}
              {m.derivation ? (
                <span className="text-[9px] leading-4 text-gray-400 dark:text-gray-500">
                  {m.derivation.publicHolidayWorkdayCount}/{m.derivation.publicHolidayCount} holiday days · {m.derivation.absenceDayEquivalent} absence days
                </span>
              ) : null}
              <span className="text-[9px] leading-4 text-gray-400 dark:text-gray-500">
                {Math.round((m.chargeabilityHours ?? (monthSahHours(m) * monthChargeabilityRatio(m))) * 10) / 10}h chg vs {Math.round((m.targetHours ?? (monthSahHours(m) * monthTargetRatio(m, r.targetPct))) * 10) / 10}h target
              </span>
            </div>
          </td>
        ))}
      </tr>
    );
  }

  function renderGroupTotalsRow(
    label: string,
    monthTotals: GroupSummary["monthTotals"],
    count: number,
    isOverall: boolean,
    onClick?: () => void,
  ) {
    const bg = isOverall
      ? "bg-brand-50/90 dark:bg-brand-900/25"
      : "bg-slate-100/90 dark:bg-slate-800/70";
    return (
      <tr
        className={`${bg} font-semibold ${onClick ? "cursor-pointer" : ""}`}
        onClick={onClick}
      >
        <td className={`sticky left-0 z-10 ${bg} px-4 py-3 text-gray-900 dark:text-gray-100`}>
          {onClick && <span className="mr-2 text-xs text-gray-500 dark:text-gray-400">{expandedGroups.has(label) ? "▾" : "▸"}</span>}
          {label} ({count} resources)
        </td>
        <td className="px-3 py-3 text-center text-gray-700 dark:text-gray-300">
          {monthTotals[0]?.totalFte.toFixed(1)}
        </td>
        <td className="px-3 py-3 text-center text-gray-700 dark:text-gray-300">
          {monthTotals[0] ? pct(monthTotals[0].target) : "—"}
        </td>
        {monthTotals.map((mt) => (
          <td key={mt.monthKey} className="px-3 py-3 text-center">
            <div className={`font-semibold ${chgColor(mt.chg, mt.target)}`}>{pct(mt.chg)}</div>
            {mt.gap !== 0 && (
              <div className="mt-0.5 text-xs text-gray-400 dark:text-gray-500">
                {mt.gap > 0 ? "+" : ""}{pct(mt.gap)}
              </div>
            )}
          </td>
        ))}
      </tr>
    );
  }

  // ─── Main render ─────────────────────────────────────────────────────────

  return (
    <div className="app-page space-y-6">
      <div className="app-page-header gap-4">
        <div className="space-y-2">
          <p className="inline-flex items-center rounded-full border border-brand-200 bg-brand-50 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-brand-700 dark:border-brand-800/80 dark:bg-brand-900/30 dark:text-brand-200">
            Forecast Report
          </p>
          <div>
            <h1 className="app-page-title" data-page-title="true">
              Chargeability Forecast
            </h1>
            <p className="app-page-subtitle">
              Review expected utilization, search specific people quickly, and compare monthly chargeability against target.
            </p>
          </div>
        </div>
        {data && data.resources.length > 0 && (
          <div className="flex flex-wrap gap-2">
            <button
              onClick={handleExportExcel}
              className="inline-flex items-center justify-center rounded-xl bg-emerald-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-emerald-700"
            >
              Export Excel
            </button>
            <button
              onClick={handleExportCsv}
              className="inline-flex items-center justify-center rounded-xl border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition hover:border-gray-400 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:hover:bg-gray-800"
            >
              Export CSV
            </button>
          </div>
        )}
      </div>

      {data ? (
        <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
          <div className="app-surface p-4">
            <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500 inline-flex items-center gap-0.5">Resources<InfoTooltip content="Number of active resources matching the current filters." /></div>
            <div className="mt-2 text-3xl font-semibold text-gray-900 dark:text-gray-100">{filteredResources.length}</div>
            <div className="mt-1 text-sm text-gray-500">People in the current filter scope</div>
          </div>
          <div className="app-surface p-4">
            <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500 inline-flex items-center gap-0.5">Average Chargeability<InfoTooltip content="FTE-weighted average chargeability across all visible resources. Formula: sum(FTE x Chg%) / sum(FTE)." /></div>
            <div className="mt-2 flex items-center gap-3">
              <ProgressRing
                value={Math.round(averageChargeability * 100)}
                size={56}
                strokeWidth={4}
                color={averageChargeability >= averageTarget ? "var(--color-green-500, #22c55e)" : averageChargeability >= averageTarget - 0.1 ? "var(--color-amber-500, #f59e0b)" : "var(--color-red-500, #ef4444)"}
              >
                <span className={`text-lg font-semibold ${chgColor(averageChargeability, averageTarget)}`}>{pct(averageChargeability)}</span>
              </ProgressRing>
            </div>
            <div className="mt-1 text-sm text-gray-500">Weighted across visible resources</div>
          </div>
          <div className="app-surface p-4">
            <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500 inline-flex items-center gap-0.5">Average Target<InfoTooltip content="FTE-weighted average chargeability target set per resource. The benchmark for actual performance." /></div>
            <div className="mt-2 text-3xl font-semibold text-gray-900 dark:text-gray-100">{pct(averageTarget)}</div>
            <div className="mt-1 text-sm text-gray-500">Planning target for the same population</div>
          </div>
          <div className="app-surface p-4">
            <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500 inline-flex items-center gap-0.5">Average Gap<InfoTooltip content="Chargeability minus target. Green = above target, red = below target." /></div>
            <div className={`mt-2 text-3xl font-semibold ${averageGap >= 0 ? "text-green-700 dark:text-green-400" : "text-red-700 dark:text-red-400"}`}>
              {averageGap > 0 ? "+" : ""}{pct(averageGap)}
            </div>
            <div className="mt-1 text-sm text-gray-500">Difference between chargeability and target</div>
          </div>
        </div>
      ) : null}

      <div className="app-toolbar">
        <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-7">
          <div>
            <label className="app-label">From</label>
            <input
              type="month"
              value={startMonth}
              onChange={(e) => setStartMonth(e.target.value)}
              className="app-input"
            />
          </div>
          <div>
            <label className="app-label">To</label>
            <input
              type="month"
              value={endMonth}
              onChange={(e) => setEndMonth(e.target.value)}
              className="app-input"
            />
          </div>
          <div>
            <label className="app-label">Country</label>
            <select
              value={countryId}
              onChange={(e) => setCountryId(e.target.value)}
              className="app-select w-full"
            >
              <option value="">All countries</option>
              {(countriesQuery.data ?? []).map((c: { id: string; code: string; name: string }) => (
                <option key={c.id} value={c.id}>{c.code} - {c.name}</option>
              ))}
            </select>
          </div>
          <div>
            <label className="app-label">Org Unit</label>
            <select
              value={orgUnitId}
              onChange={(e) => setOrgUnitId(e.target.value)}
              className="app-select w-full"
            >
              <option value="">All org units</option>
              {orgUnits.map((u: { id: string; name: string }) => (
                <option key={u.id} value={u.id}>{u.name}</option>
              ))}
            </select>
          </div>
          <div>
            <label className="app-label">Mgmt Level Group</label>
            <select
              value={mgmtGroupId}
              onChange={(e) => setMgmtGroupId(e.target.value)}
              className="app-select w-full"
            >
              <option value="">All groups</option>
              {(mgmtGroupsQuery.data ?? []).map((g: { id: string; name: string }) => (
                <option key={g.id} value={g.id}>{g.name}</option>
              ))}
            </select>
          </div>
          <div>
            <label className="app-label">Group By</label>
            <select
              value={groupBy}
              onChange={(e) => { setGroupBy(e.target.value as GroupByField); setExpandedGroups(new Set()); }}
              className="app-select w-full"
            >
              <option value="none">No grouping</option>
              <option value="orgUnit">Org unit</option>
              <option value="mgmtGroup">Mgmt level group</option>
              <option value="country">Country</option>
            </select>
          </div>
          <div className="md:col-span-2 xl:col-span-1 2xl:col-span-1">
            <label className="app-label">Name Search</label>
            <input
              type="search"
              value={nameSearch}
              onChange={(e) => setNameSearch(e.target.value)}
              placeholder="Search by name or EID"
              className="app-input"
            />
          </div>
        </div>

        <div className="mt-4 flex flex-wrap items-center justify-between gap-3 border-t border-gray-200 pt-4 dark:border-gray-700">
          <label className="inline-flex items-center gap-3 text-sm font-medium text-gray-700 dark:text-gray-300">
            <input
              type="checkbox"
              checked={includeProposed}
              onChange={(e) => setIncludeProposed(e.target.checked)}
              className="h-4 w-4 rounded border-gray-300 text-brand-600 focus:ring-brand-500"
            />
            <span>Include proposed work in utilization calculations</span>
          </label>
          <div className="text-xs uppercase tracking-[0.16em] text-gray-500 dark:text-gray-400">
            {groupBy === "none" ? "Flat resource view" : `Grouped by ${groupBy}`}
          </div>
        </div>
      </div>

      {data?.explainability ? (
        <div className="app-surface p-4">
          <div className="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
            <div className="space-y-2">
              <div className="inline-flex items-center gap-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500">
                Calculation Basis
                <InfoTooltip content="The same holiday, absence, and SAH logic used by the API and assistant." />
              </div>
              <div className="text-sm text-gray-600 dark:text-gray-300">
                SAH = base available hours minus public-holiday deduction minus absence deduction. Exported rows already include Country, State, City, and monthly Base/Holiday/Absence/SAH values.
              </div>
            </div>
            <div className="text-xs text-gray-500 dark:text-gray-400">
              Excel adds an Explainability sheet with formulas, notes, and active filters.
            </div>
          </div>

          <div className="mt-4 grid gap-4 lg:grid-cols-3">
            <div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-800 dark:bg-slate-900/60">
              <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Location basis</div>
              <div className="mt-2 flex flex-wrap gap-2">
                {data.explainability.locationFields.map((field) => (
                  <span key={field} className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-600 dark:border-gray-700 dark:bg-slate-950 dark:text-gray-300">
                    {toTitleLabel(field)}
                  </span>
                ))}
              </div>
            </div>

            <div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-800 dark:bg-slate-900/60">
              <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Month derivation</div>
              <div className="mt-2 flex flex-wrap gap-2">
                {data.explainability.monthDerivationFields.map((field) => (
                  <span key={field} className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-600 dark:border-gray-700 dark:bg-slate-950 dark:text-gray-300">
                    {toTitleLabel(field)}
                  </span>
                ))}
              </div>
            </div>

            <div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-800 dark:bg-slate-900/60">
              <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Active filters</div>
              <div className="mt-2 flex flex-wrap gap-2">
                {data.explainability.activeFilters.length > 0 ? data.explainability.activeFilters.map((field) => (
                  <span key={field} className="rounded-full border border-brand-200 bg-brand-50 px-2 py-1 text-xs text-brand-700 dark:border-brand-800 dark:bg-brand-900/30 dark:text-brand-200">
                    {toTitleLabel(field)}
                  </span>
                )) : (
                  <span className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-500 dark:border-gray-700 dark:bg-slate-950 dark:text-gray-400">
                    none
                  </span>
                )}
              </div>
              <div className="mt-3 space-y-1 text-xs text-gray-500 dark:text-gray-400">
                <div>Chargeability %: {data.explainability.formulas.chargeabilityPct}</div>
                <div>Target hours: {data.explainability.formulas.targetHours}</div>
                <div>Gap hours: {data.explainability.formulas.gapHours}</div>
              </div>
            </div>
          </div>
        </div>
      ) : null}

      {reportQuery.isLoading && !data ? (
        <div className="app-surface-strong py-16 text-center text-sm text-gray-500">Loading report...</div>
      ) : reportQuery.error ? (
        <div className="app-surface-strong py-16 text-center text-sm text-red-600 dark:text-red-400">
          Error: {reportQuery.error.message}
        </div>
      ) : data && data.resources.length === 0 ? (
        <div className="app-surface-strong py-16 text-center text-sm text-gray-500">
          No resources match the current filters.
        </div>
      ) : data && filteredResources.length === 0 ? (
        <div className="app-surface-strong py-16 text-center text-sm text-gray-500">
          No resources match the current search.
        </div>
      ) : data ? (
        <div className="app-data-table">
          <div className="overflow-x-auto">
            <table className="min-w-full text-sm">
              <thead>
                <tr>
                  <th className="sticky left-0 z-10 min-w-[240px] bg-gray-50/95 px-4 py-3 text-left backdrop-blur dark:bg-gray-800/95">
                    Resource
                  </th>
                  <th className="w-20 px-3 py-3 text-center"><span className="inline-flex items-center justify-center gap-0.5">FTE<InfoTooltip content="Full-Time Equivalent. 1.0 = full-time, 0.5 = half-time. Used to weight chargeability averages." /></span></th>
                  <th className="w-24 px-3 py-3 text-center"><span className="inline-flex items-center justify-center gap-0.5">Target<InfoTooltip content="Chargeability target for this resource. Cells are green/yellow/red relative to this target." /></span></th>
                  {data.monthKeys.map((key) => (
                    <th key={key} className="min-w-[96px] px-3 py-3 text-center">
                      {formatMonth(key)}
                    </th>
                  ))}
                </tr>
              </thead>
              <tbody className="divide-y divide-gray-100 dark:divide-gray-800">
                {renderGroupTotalsRow("Group Total", filteredGroupTotals, filteredResources.length, true)}

                {groupBy !== "none" ? (
                  groups.map((g) => (
                    <React.Fragment key={g.label}>
                      {renderGroupTotalsRow(g.label, g.monthTotals, g.resources.length, false, () => toggleGroup(g.label))}
                      {expandedGroups.has(g.label) && g.resources.map((r) => (
                        <React.Fragment key={r.id}>
                          {renderResourceRow(r)}
                          {renderExpandedRow(r)}
                        </React.Fragment>
                      ))}
                    </React.Fragment>
                  ))
                ) : (
                  filteredResources.map((r) => (
                    <React.Fragment key={r.id}>
                      {renderResourceRow(r)}
                      {renderExpandedRow(r)}
                    </React.Fragment>
                  ))
                )}
              </tbody>
            </table>
          </div>
        </div>
      ) : null}
    </div>
  );
}
