"use client";

import { useEffect, useMemo, useRef, useState, type ReactNode, type UIEvent } from "react";
import { trpc } from "~/lib/trpc/client.js";
import type { WidgetProps } from "~/components/dashboard/widget-registry.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";

type TopSortKey = "name" | "actual" | "expected";
type WatchSortKey = "name" | "actual" | "target";

type CountryOption = {
  id: string;
  name: string;
};

type ChargeabilityRow = {
  id: string;
  displayName: string;
  chapter: string | null;
  chargeabilityTarget: number;
  actualChargeability: number;
  expectedChargeability: number;
};

function FilterDropdown({ label, children }: { label: string; children: ReactNode }) {
  const [isOpen, setIsOpen] = useState(false);
  const dropdownRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    function handlePointerDown(event: MouseEvent) {
      const target = event.target as Node;
      if (dropdownRef.current && !dropdownRef.current.contains(target)) {
        setIsOpen(false);
      }
    }

    document.addEventListener("mousedown", handlePointerDown);
    return () => document.removeEventListener("mousedown", handlePointerDown);
  }, []);

  return (
    <div ref={dropdownRef} className="relative">
      <button
        type="button"
        onClick={() => setIsOpen((current) => !current)}
        className="inline-flex min-w-44 items-center justify-between gap-3 rounded-xl border border-gray-300 bg-white px-3 py-2 text-xs text-gray-700 shadow-sm transition hover:border-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200"
      >
        <span className="truncate">{label}</span>
        <span className="text-[10px] text-gray-400">{isOpen ? "▲" : "▼"}</span>
      </button>
      {isOpen ? (
        <div className="absolute right-0 z-20 mt-2 w-72 rounded-2xl border border-gray-200 bg-white p-3 shadow-lg dark:border-gray-700 dark:bg-gray-900">
          {children}
        </div>
      ) : null}
    </div>
  );
}

export function ChargeabilityWidget({ config: _config }: WidgetProps) {
  const config = _config as { topN?: number; watchlistThreshold?: number };
  const [includeProposed, setIncludeProposed] = useState(false);
  const [showDeparted, setShowDeparted] = useState(false);
  const [selectedCountryIds, setSelectedCountryIds] = useState<string[]>([]);
  const [topSort, setTopSort] = useState<TopSortKey>("actual");
  const [topDir, setTopDir] = useState<"asc" | "desc">("desc");
  const [watchSort, setWatchSort] = useState<WatchSortKey>("actual");
  const [watchDir, setWatchDir] = useState<"asc" | "desc">("asc");
  const batchSize = Math.max(config.topN ?? 10, 10);
  const [topVisibleCount, setTopVisibleCount] = useState(batchSize);
  const [watchVisibleCount, setWatchVisibleCount] = useState(batchSize);

  const { data: countriesData } = trpc.country.list.useQuery(undefined, { staleTime: 60_000 });
  const countries = useMemo(
    () =>
      ((countriesData ?? []) as Array<{ id: string; name: string }>).map((country) => ({
        id: country.id,
        name: country.name,
      })),
    [countriesData],
  ) as CountryOption[];
  const selectedCountryLabel = useMemo(() => {
    if (selectedCountryIds.length === 0) return "Countries: All";
    if (selectedCountryIds.length === 1) {
      return `Country: ${countries.find((country) => country.id === selectedCountryIds[0])?.name ?? "1 selected"}`;
    }
    return `Countries: ${selectedCountryIds.length} selected`;
  }, [countries, selectedCountryIds]);

  function toggleTop(key: TopSortKey) {
    if (topSort === key) setTopDir((d) => (d === "asc" ? "desc" : "asc"));
    else {
      setTopSort(key);
      setTopDir(key === "name" ? "asc" : "desc");
    }
  }
  function toggleWatch(key: WatchSortKey) {
    if (watchSort === key) setWatchDir((d) => (d === "asc" ? "desc" : "asc"));
    else {
      setWatchSort(key);
      setWatchDir(key === "name" ? "asc" : "asc");
    }
  }

  const { data, isLoading } = trpc.dashboard.getChargeabilityOverview.useQuery(
    {
      includeProposed,
      topN: config.topN ?? 10,
      watchlistThreshold: config.watchlistThreshold ?? 15,
      ...(selectedCountryIds.length > 0 ? { countryIds: selectedCountryIds } : {}),
      ...(!showDeparted ? { departed: false } : {}),
    },
    { staleTime: 60_000, placeholderData: (prev) => prev },
  );

  useEffect(() => {
    setTopVisibleCount(batchSize);
    setWatchVisibleCount(batchSize);
  }, [batchSize, includeProposed, selectedCountryIds, showDeparted]);

  if (isLoading) {
    return (
      <div className="animate-pulse flex flex-col gap-3 pt-1">
        <div className="h-2 w-32 bg-gray-200 dark:bg-gray-700 rounded" />
        {[...Array(4)].map((_, i) => (
          <div key={i} className="flex gap-3 px-2 py-1">
            <div className="h-3 w-4 bg-gray-200 dark:bg-gray-700 rounded" />
            <div className="h-3 flex-1 bg-gray-200 dark:bg-gray-700 rounded" />
            <div className="h-3 w-10 bg-gray-200 dark:bg-gray-700 rounded" />
            <div className="h-3 w-10 bg-gray-200 dark:bg-gray-700 rounded" />
          </div>
        ))}
        <div className="border-t border-gray-100 dark:border-gray-800 mt-1 pt-2">
          <div className="h-2 w-20 bg-gray-200 dark:bg-gray-700 rounded mb-2" />
          {[...Array(3)].map((_, i) => (
            <div key={i} className="flex gap-3 px-2 py-1">
              <div className="h-3 flex-1 bg-gray-200 dark:bg-gray-700 rounded" />
              <div className="h-3 w-10 bg-gray-100 dark:bg-gray-800 rounded" />
              <div className="h-3 w-10 bg-gray-100 dark:bg-gray-800 rounded" />
            </div>
          ))}
        </div>
      </div>
    );
  }

  const rawTop = data?.top ?? [];
  const rawWatch = data?.watchlist ?? [];
  const month = data?.month ?? "";

  const top = ([...rawTop] as ChargeabilityRow[]).sort((a, b) => {
    const mult = topDir === "asc" ? 1 : -1;
    switch (topSort) {
      case "name":
        return mult * a.displayName.localeCompare(b.displayName);
      case "actual":
        return mult * (a.actualChargeability - b.actualChargeability);
      case "expected":
        return mult * (a.expectedChargeability - b.expectedChargeability);
      default:
        return 0;
    }
  });

  const watchlist = ([...rawWatch] as ChargeabilityRow[]).sort((a, b) => {
    const mult = watchDir === "asc" ? 1 : -1;
    switch (watchSort) {
      case "name":
        return mult * a.displayName.localeCompare(b.displayName);
      case "actual":
        return mult * (a.actualChargeability - b.actualChargeability);
      case "target":
        return mult * (a.chargeabilityTarget - b.chargeabilityTarget);
      default:
        return 0;
    }
  });

  function TopInd({ k }: { k: TopSortKey }) {
    return topSort === k ? (
      <span className="text-[10px] ml-0.5">{topDir === "asc" ? "▲" : "▼"}</span>
    ) : (
      <span className="text-[10px] ml-0.5 text-gray-300">⇅</span>
    );
  }
  function WatchInd({ k }: { k: WatchSortKey }) {
    return watchSort === k ? (
      <span className="text-[10px] ml-0.5">{watchDir === "asc" ? "▲" : "▼"}</span>
    ) : (
      <span className="text-[10px] ml-0.5 text-gray-300">⇅</span>
    );
  }

  const visibleTop = top.slice(0, topVisibleCount);
  const visibleWatchlist = watchlist.slice(0, watchVisibleCount);

  function handleSectionScroll(
    event: UIEvent<HTMLElement>,
    visibleCount: number,
    totalCount: number,
    setVisibleCount: (value: number | ((current: number) => number)) => void,
  ) {
    const element = event.currentTarget;
    const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;
    if (distanceFromBottom > 48 || visibleCount >= totalCount) {
      return;
    }
    setVisibleCount((current) => Math.min(current + batchSize, totalCount));
  }

  function toggleCountry(countryId: string, checked: boolean) {
    setSelectedCountryIds((current) => {
      if (checked) {
        return current.includes(countryId) ? current : [...current, countryId];
      }
      return current.filter((id) => id !== countryId);
    });
  }

  return (
    <div className="h-full flex flex-col gap-2 overflow-hidden">
      {month && (
        <div className="px-1 flex-shrink-0 flex flex-col gap-2">
          <div className="flex items-center justify-between gap-3">
            <p className="text-xs text-gray-400 flex items-center gap-1">
              Period: {month}
              <InfoTooltip
                content="Chargeability is calculated for the current calendar month. Available hours are based on each person's weekly schedule (WeekdayAvailability). Watchlist threshold: 15 percentage points below target."
                width="w-72"
              />
            </p>
            <label className="flex items-center gap-2 rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 text-xs text-gray-600 dark:border-gray-700 dark:bg-gray-900/70 dark:text-gray-300">
              <input
                type="checkbox"
                checked={includeProposed}
                onChange={(event) => setIncludeProposed(event.target.checked)}
                className="rounded border-gray-300"
              />
              Include proposed
            </label>
          </div>
          <div className="flex flex-wrap items-center gap-2">
            <label className="flex items-center gap-2 rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 text-xs text-gray-600 dark:border-gray-700 dark:bg-gray-900/70 dark:text-gray-300">
              <input
                type="checkbox"
                checked={showDeparted}
                onChange={(event) => setShowDeparted(event.target.checked)}
                className="rounded border-gray-300"
              />
              Show departed
            </label>
            <FilterDropdown label={selectedCountryLabel}>
              <div className="space-y-2">
                <div className="flex items-center justify-between">
                  <p className="text-xs font-medium text-gray-600">Countries</p>
                  {selectedCountryIds.length > 0 ? (
                    <button
                      type="button"
                      onClick={() => setSelectedCountryIds([])}
                      className="text-[11px] text-brand-600 hover:text-brand-700"
                    >
                      Clear
                    </button>
                  ) : null}
                </div>
                <p className="text-[11px] text-gray-400">
                  Empty selection means all countries are included.
                </p>
                <div className="max-h-48 space-y-1 overflow-y-auto pr-1">
                  {countries.map((country) => (
                    <label
                      key={country.id}
                      className="flex items-center gap-2 text-xs text-gray-700"
                    >
                      <input
                        type="checkbox"
                        checked={selectedCountryIds.includes(country.id)}
                        onChange={(event) => toggleCountry(country.id, event.target.checked)}
                        className="rounded border-gray-300"
                      />
                      <span>{country.name}</span>
                    </label>
                  ))}
                </div>
              </div>
            </FilterDropdown>
          </div>
        </div>
      )}

      {/* Top list */}
      <section
        className="flex-1 min-h-0 overflow-auto"
        onScroll={(event) =>
          handleSectionScroll(event, topVisibleCount, top.length, setTopVisibleCount)
        }
      >
        <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider px-1 mb-1 sticky top-0 bg-white">
          Top Chargeability
          <span className="ml-1 font-normal normal-case text-gray-400">
            {visibleTop.length}/{top.length}
          </span>
        </h3>
        {top.length === 0 ? (
          <p className="text-xs text-gray-400 px-1">No data available.</p>
        ) : (
          <table className="w-full text-xs">
            <thead>
              <tr className="text-gray-400 border-b border-gray-100">
                <th className="px-2 py-1 text-left font-medium w-6">#</th>
                <th className="px-2 py-1 text-left font-medium">
                  <button
                    type="button"
                    onClick={() => toggleTop("name")}
                    className="inline-flex items-center hover:text-gray-600 cursor-pointer"
                  >
                    Name
                    <TopInd k="name" />
                  </button>
                </th>
                <th className="px-2 py-1 text-right font-medium">
                  <span className="inline-flex items-center justify-end">
                    <button
                      type="button"
                      onClick={() => toggleTop("actual")}
                      className="inline-flex items-center hover:text-gray-600 cursor-pointer"
                    >
                      Actual
                      <TopInd k="actual" />
                    </button>
                    <InfoTooltip
                      content="Actual uses CONFIRMED and ACTIVE bookings on active projects. Turn on 'Include proposed' to also count proposed work and imported TBD planning."
                      width="w-72"
                    />
                  </span>
                </th>
                <th className="px-2 py-1 text-right font-medium">
                  <span className="inline-flex items-center justify-end">
                    <button
                      type="button"
                      onClick={() => toggleTop("expected")}
                      className="inline-flex items-center hover:text-gray-600 cursor-pointer"
                    >
                      Expected
                      <TopInd k="expected" />
                    </button>
                    <InfoTooltip
                      content="Expected includes all non-CANCELLED bookings this month, including draft projects and proposed planning rows."
                      width="w-72"
                    />
                  </span>
                </th>
              </tr>
            </thead>
            <tbody className="divide-y divide-gray-50">
              {visibleTop.map((r, i) => (
                <tr key={r.id} className="hover:bg-gray-50">
                  <td className="px-2 py-1 text-gray-400">{i + 1}</td>
                  <td className="px-2 py-1 text-gray-800 truncate max-w-[120px]">
                    <span title={r.displayName}>{r.displayName}</span>
                    {r.chapter && <span className="text-gray-400 ml-1">· {r.chapter}</span>}
                  </td>
                  <td className="px-2 py-1 text-right font-semibold text-green-700">
                    {r.actualChargeability}%
                  </td>
                  <td className="px-2 py-1 text-right text-gray-400">{r.expectedChargeability}%</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </section>

      <div className="border-t border-gray-100 flex-shrink-0" />

      {/* Watchlist */}
      <section
        className="flex-1 min-h-0 overflow-auto"
        onScroll={(event) =>
          handleSectionScroll(event, watchVisibleCount, watchlist.length, setWatchVisibleCount)
        }
      >
        <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider px-1 mb-1 sticky top-0 bg-white">
          Watchlist <span className="font-normal text-gray-400">(below target)</span>
          <span className="ml-1 font-normal normal-case text-gray-400">
            {visibleWatchlist.length}/{watchlist.length}
          </span>
        </h3>
        {watchlist.length === 0 ? (
          <p className="text-xs text-gray-400 px-1">All resources at or near target.</p>
        ) : (
          <table className="w-full text-xs">
            <thead>
              <tr className="text-gray-400 border-b border-gray-100">
                <th className="px-2 py-1 text-left font-medium">
                  <button
                    type="button"
                    onClick={() => toggleWatch("name")}
                    className="inline-flex items-center hover:text-gray-600 cursor-pointer"
                  >
                    Name
                    <WatchInd k="name" />
                  </button>
                </th>
                <th className="px-2 py-1 text-right font-medium">
                  <span className="inline-flex items-center justify-end">
                    <button
                      type="button"
                      onClick={() => toggleWatch("actual")}
                      className="inline-flex items-center hover:text-gray-600 cursor-pointer"
                    >
                      Actual
                      <WatchInd k="actual" />
                    </button>
                    <InfoTooltip content="Actual chargeability this month. By default this counts CONFIRMED and ACTIVE bookings on ACTIVE projects; the toggle can also include PROPOSED work." />
                  </span>
                </th>
                <th className="px-2 py-1 text-right font-medium">
                  <span className="inline-flex items-center justify-end">
                    <button
                      type="button"
                      onClick={() => toggleWatch("target")}
                      className="inline-flex items-center hover:text-gray-600 cursor-pointer"
                    >
                      Target
                      <WatchInd k="target" />
                    </button>
                    <InfoTooltip content="Chargeability target set by management. Watchlist shows resources more than 15 percentage points below their target." />
                  </span>
                </th>
              </tr>
            </thead>
            <tbody className="divide-y divide-gray-50">
              {visibleWatchlist.map((r) => (
                <tr key={r.id} className="hover:bg-gray-50">
                  <td className="px-2 py-1 text-gray-800 truncate max-w-[140px]">
                    <span title={r.displayName}>{r.displayName}</span>
                    {r.chapter && <span className="text-gray-400 ml-1">· {r.chapter}</span>}
                  </td>
                  <td className="px-2 py-1 text-right font-semibold text-red-600">
                    {r.actualChargeability}%
                  </td>
                  <td className="px-2 py-1 text-right text-gray-400">{r.chargeabilityTarget}%</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </section>
    </div>
  );
}
