"use client";

import { useMemo } from "react";
import dynamic from "next/dynamic";
import { trpc } from "~/lib/trpc/client.js";
import type { WidgetProps } from "~/components/dashboard/widget-registry.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";

const PeakTimesChart = dynamic(
  () => import("~/components/dashboard/widgets/PeakTimesChart.js"),
  { ssr: false, loading: () => <div className="flex-1 shimmer-skeleton rounded-xl" /> },
);

type PeakDepartmentRow = {
  name: string;
  hours: number;
  capacityHours: number;
  remainingHours: number;
  overbookedHours: number;
  utilizationPct: number;
};

type PeakCalendarLocation = {
  countryCode: string | null;
  countryName: string | null;
  federalState: string | null;
  metroCityName: string | null;
  resourceCount: number;
  effectiveAvailableHours: number;
};

type PeakPeriodRow = {
  period: string;
  label: string;
  bookedHours: number;
  capacityHours: number;
  baseAvailableHours: number;
  holidayHoursDeduction: number;
  absenceDayEquivalent: number;
  absenceHoursDeduction: number;
  remainingHours: number;
  overbookedHours: number;
  utilizationPct: number;
  isCurrentPeriod: boolean;
  calendarContextCount: number;
  calendarLocations: PeakCalendarLocation[];
  groups: PeakDepartmentRow[];
};

function formatHours(value: number): string {
  return new Intl.NumberFormat("de-DE", {
    minimumFractionDigits: 0,
    maximumFractionDigits: 1,
  }).format(value);
}

function formatDayEquivalent(value: number): string {
  return Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1);
}

function formatMonthLabel(periodStart: string | undefined, fallback: string): string {
  if (!periodStart) {
    return fallback;
  }

  const date = new Date(`${periodStart}T00:00:00.000Z`);
  if (Number.isNaN(date.getTime())) {
    return fallback;
  }

  return new Intl.DateTimeFormat("en-US", {
    month: "short",
    year: "2-digit",
    timeZone: "UTC",
  }).format(date);
}

function utilizationTone(utilizationPct: number): string {
  if (utilizationPct >= 100) return "bg-red-500";
  if (utilizationPct >= 85) return "bg-amber-400";
  return "bg-emerald-500";
}

function utilizationTextTone(utilizationPct: number): string {
  if (utilizationPct >= 100) return "text-red-600 dark:text-red-300";
  if (utilizationPct >= 85) return "text-amber-600 dark:text-amber-300";
  return "text-emerald-600 dark:text-emerald-300";
}

function compactMetricLabel(row: PeakPeriodRow | null | undefined, fallback: string): string {
  if (!row) {
    return fallback;
  }

  return `${row.label} · ${row.utilizationPct}%`;
}

function formatLocation(location: PeakCalendarLocation): string {
  const parts = [location.countryCode ?? location.countryName, location.federalState, location.metroCityName]
    .filter((part): part is string => Boolean(part));
  return parts.length > 0 ? parts.join(" / ") : "No calendar context";
}

function formatCapacityBasis(row: PeakPeriodRow | null | undefined): string {
  if (!row) {
    return "No capacity basis";
  }

  return [
    `${formatHours(row.baseAvailableHours)}h base`,
    `- ${formatHours(row.holidayHoursDeduction)}h holidays`,
    `- ${formatHours(row.absenceHoursDeduction)}h absences (${formatDayEquivalent(row.absenceDayEquivalent)}d)`,
    `= ${formatHours(row.capacityHours)}h effective`,
  ].join(" ");
}

function formatCalendarBasis(row: PeakPeriodRow | null | undefined): string {
  if (!row) {
    return "No calendar basis";
  }

  if (row.calendarLocations.length === 0) {
    return "No location-based calendar basis";
  }

  return row.calendarLocations
    .slice(0, 3)
    .map((location) =>
      `${formatLocation(location)} (${location.resourceCount}x · ${formatHours(location.effectiveAvailableHours)}h)`)
    .join(" · ");
}

function aggregateDepartmentRows(rows: PeakDepartmentRow[], limit = 6): PeakDepartmentRow[] {
  if (rows.length <= limit) {
    return rows;
  }

  const visibleRows = rows.slice(0, limit - 1);
  const hiddenRows = rows.slice(limit - 1);
  const hiddenHours = hiddenRows.reduce((sum, row) => sum + row.hours, 0);
  const hiddenCapacityHours = hiddenRows.reduce((sum, row) => sum + row.capacityHours, 0);
  const hiddenRemainingHours = hiddenRows.reduce((sum, row) => sum + row.remainingHours, 0);
  const hiddenOverbookedHours = hiddenRows.reduce((sum, row) => sum + row.overbookedHours, 0);

  return [
    ...visibleRows,
    {
      name: `Other (${hiddenRows.length})`,
      hours: hiddenHours,
      capacityHours: hiddenCapacityHours,
      remainingHours: hiddenRemainingHours,
      overbookedHours: hiddenOverbookedHours,
      utilizationPct:
        hiddenCapacityHours > 0 ? Math.round((hiddenHours / hiddenCapacityHours) * 100) : 0,
    },
  ];
}

export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
  const showDetails = config.showDetails === true;
  const now = new Date();
  const startDate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)).toISOString();
  const endDate = new Date(
    Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 12, 0, 23, 59, 59, 999),
  ).toISOString();
  const currentPeriodKey = `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
  const persistedPeriod = typeof config.activePeriod === "string" ? config.activePeriod : null;

  const { data, isLoading } = trpc.dashboard.getPeakTimes.useQuery(
    { startDate, endDate, granularity: "month", groupBy: "chapter" },
    { staleTime: 120_000, placeholderData: (prev) => prev },
  );

  const periods = useMemo<PeakPeriodRow[]>(
    () =>
      (data ?? []).map((period) => {
        const derivation = period.derivation;
        const bookedHours = period.bookedHours ?? derivation.bookedHours ?? period.totalHours;
        const capacityHours = period.capacityHours ?? derivation.capacityHours ?? 0;
        const baseAvailableHours = derivation.baseAvailableHours ?? capacityHours;
        const holidayHoursDeduction = derivation.publicHolidayHoursDeduction ?? 0;
        const absenceDayEquivalent = derivation.absenceDayEquivalent ?? 0;
        const absenceHoursDeduction = derivation.absenceHoursDeduction ?? 0;
        const remainingHours =
          period.remainingHours ??
          derivation.remainingCapacityHours ??
          Math.max(capacityHours - bookedHours, 0);
        const overbookedHours =
          period.overbookedHours ??
          derivation.overbookedHours ??
          Math.max(bookedHours - capacityHours, 0);
        const utilizationPct =
          period.utilizationPct ??
          derivation.utilizationPct ??
          (capacityHours > 0 ? Math.round((bookedHours / capacityHours) * 100) : 0);

        return {
          period: period.period,
          label: formatMonthLabel(period.periodStart ?? derivation.periodStart, period.period),
          bookedHours,
          capacityHours,
          baseAvailableHours,
          holidayHoursDeduction,
          absenceDayEquivalent,
          absenceHoursDeduction,
          remainingHours,
          overbookedHours,
          utilizationPct,
          isCurrentPeriod: period.period === currentPeriodKey,
          calendarContextCount: derivation.calendarContextCount ?? 0,
          calendarLocations: derivation.calendarLocations ?? [],
          groups: (period.groups ?? [])
            .map((group) => {
              const groupCapacityHours = group.capacityHours ?? 0;
              const groupRemainingHours =
                group.remainingHours ?? Math.max(groupCapacityHours - group.hours, 0);
              const groupOverbookedHours =
                group.overbookedHours ?? Math.max(group.hours - groupCapacityHours, 0);
              const groupUtilizationPct =
                group.utilizationPct ??
                (groupCapacityHours > 0 ? Math.round((group.hours / groupCapacityHours) * 100) : 0);

              return {
                name: group.name,
                hours: group.hours,
                capacityHours: groupCapacityHours,
                remainingHours: groupRemainingHours,
                overbookedHours: groupOverbookedHours,
                utilizationPct: groupUtilizationPct,
              };
            })
            .sort(
              (left, right) =>
                right.utilizationPct - left.utilizationPct ||
                right.hours - left.hours ||
                left.name.localeCompare(right.name),
            ),
        };
      }),
    [currentPeriodKey, data],
  );

  const selectedPeriod =
    (persistedPeriod && periods.some((period) => period.period === persistedPeriod) ? persistedPeriod : null) ??
    (periods.some((period) => period.period === currentPeriodKey) ? currentPeriodKey : periods[0]?.period ?? null);

  const selectedPeriodRow =
    periods.find((period) => period.period === selectedPeriod) ?? periods[0] ?? null;
  const currentPeriodRow =
    periods.find((period) => period.period === currentPeriodKey) ?? selectedPeriodRow;
  const peakPeriodRow = useMemo(
    () =>
      [...periods].sort(
        (left, right) =>
          right.utilizationPct - left.utilizationPct || right.bookedHours - left.bookedHours,
      )[0] ?? null,
    [periods],
  );
  const departmentRows = useMemo(
    () => aggregateDepartmentRows(selectedPeriodRow?.groups ?? []),
    [selectedPeriodRow],
  );

  if (isLoading && periods.length === 0) {
    return (
      <div className="flex h-full flex-col gap-3 pt-2">
        <div className="flex gap-2">
          {[...Array(3)].map((_, index) => (
            <div key={index} className="h-10 flex-1 rounded-full shimmer-skeleton" />
          ))}
        </div>
        <div className="flex-1 rounded-[22px] shimmer-skeleton" />
        <div className="h-32 rounded-[22px] shimmer-skeleton" />
      </div>
    );
  }

  return (
    <div className="flex h-full flex-col gap-2 overflow-hidden">
      <div className="flex items-center justify-between gap-3">
        <div className="flex min-w-0 flex-1 flex-wrap gap-2">
          {[
            { label: "Current", row: currentPeriodRow, fallback: "No data" },
            { label: "Pinned", row: selectedPeriodRow, fallback: "Hover or pin" },
            { label: "Peak", row: peakPeriodRow, fallback: "No data" },
          ].map((metric) => (
            <div
              key={metric.label}
              className="min-w-0 flex-1 rounded-full border border-slate-200/80 bg-white/85 px-3 py-1.5 shadow-sm dark:border-slate-700/70 dark:bg-slate-900/60"
            >
              <div className="flex items-center justify-between gap-2">
                <span className="text-[10px] font-semibold uppercase tracking-[0.14em] text-slate-400">
                  {metric.label}
                </span>
                <span className={`shrink-0 text-xs font-semibold ${utilizationTextTone(metric.row?.utilizationPct ?? 0)}`}>
                  {metric.row?.utilizationPct ?? 0}%
                </span>
              </div>
              {showDetails ? (
                <div className="mt-0.5 truncate text-[10px] text-slate-500 dark:text-slate-400">
                  {compactMetricLabel(metric.row, metric.fallback)}
                </div>
              ) : null}
            </div>
          ))}
        </div>
        {showDetails ? (
          <InfoTooltip
            content={
              <span>
                The top chart shows total booked load against effective capacity.<br />
                Effective capacity is base availability minus regional holidays and approved absences.<br />
                Hover any month to inspect details and click to pin the department breakdown.
              </span>
            }
            width="w-80"
            position="bottom"
          />
        ) : null}
      </div>

      <div className="min-h-0 flex-1 lg:grid lg:grid-cols-[minmax(0,1.85fr)_minmax(18rem,0.95fr)] lg:gap-3">
        <div className="min-h-0">
          <PeakTimesChart
            rows={periods}
            selectedPeriod={selectedPeriod}
            onSelectedPeriodChange={(period) => onConfigChange?.({ activePeriod: period })}
          />
        </div>

        <div className="mt-2 min-h-0 lg:mt-0">
          <div className="flex h-full flex-col rounded-[22px] border border-slate-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-slate-700/70 dark:bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(15,23,42,0.98))]">
            <div className="flex flex-wrap items-start justify-between gap-2 border-b border-slate-200/70 pb-2 dark:border-slate-700/60">
              <div>
                <div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-slate-400">
                  Department Utilization
                </div>
                {showDetails ? (
                <div className="text-xs text-slate-500 dark:text-slate-400">
                  {selectedPeriodRow?.label ?? "No active month"}
                </div>
              ) : null}
            </div>
            {showDetails ? (
              <div className="text-right text-[11px] text-slate-500 dark:text-slate-400">
                <div>{selectedPeriodRow ? `${formatHours(selectedPeriodRow.bookedHours)}h booked` : "No load"}</div>
                <div>{selectedPeriodRow ? `${formatHours(selectedPeriodRow.capacityHours)}h capacity` : ""}</div>
                {selectedPeriodRow ? (
                  <div>{selectedPeriodRow.calendarContextCount} calendar base{selectedPeriodRow.calendarContextCount === 1 ? "" : "s"}</div>
                ) : null}
                {selectedPeriodRow ? (
                  <div>{formatCapacityBasis(selectedPeriodRow)}</div>
                ) : null}
                {selectedPeriodRow ? (
                  <div>{formatCalendarBasis(selectedPeriodRow)}</div>
                ) : null}
              </div>
            ) : null}
          </div>

            <div className="mt-3 min-h-0 flex-1 space-y-2 overflow-auto pr-1">
              {departmentRows.length > 0 ? (
                departmentRows.map((group) => {
                  const barWidth = Math.min(group.utilizationPct, 100);
                  return (
                    <div key={group.name} className="space-y-1">
                      <div className="flex items-center justify-between gap-3">
                        <div className="min-w-0 truncate text-xs font-medium text-slate-700 dark:text-slate-200">
                          {group.name}
                        </div>
                        <div className={`shrink-0 text-[11px] font-semibold ${utilizationTextTone(group.utilizationPct)}`}>
                          {group.utilizationPct}%
                        </div>
                      </div>
                      <div
                        className="relative h-2.5 overflow-visible rounded-full bg-slate-100 dark:bg-slate-800/80"
                        title={`${group.name}: ${group.utilizationPct}% utilization, ${formatHours(group.hours)}h booked, ${formatHours(group.capacityHours)}h capacity, ${formatHours(group.remainingHours)}h free, ${formatHours(group.overbookedHours)}h overbooked`}
                      >
                        <div
                          className={`h-full rounded-full ${utilizationTone(group.utilizationPct)}`}
                          style={{ width: `${barWidth}%` }}
                        />
                        {group.overbookedHours > 0 ? (
                          <div
                            className="absolute right-0 top-0 h-full rounded-full bg-red-600/85"
                            style={{ width: `${Math.min(22, Math.max(8, group.utilizationPct - 100))}%` }}
                          />
                        ) : null}
                      </div>
                    </div>
                  );
                })
              ) : (
                <div className="rounded-2xl border border-dashed border-slate-200 bg-slate-50/80 px-3 py-4 text-sm text-slate-400 dark:border-slate-700 dark:bg-slate-900/40">
                  No department data in the selected month.
                </div>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
