"use client";

import { useCallback, useState } from "react";
import dynamic from "next/dynamic";
import Link from "next/link";
import { useQuery } from "@tanstack/react-query";
import { AlertTriangle, MapPin, Radio } from "lucide-react";
import { PageHeader } from "@/components/shared/page-header";
import { EmptyState, ErrorState, TableSkeleton } from "@/components/shared/states";
import { StatusBadge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { getLiveFleet, gpsTone, LIVE_FLEET_POLL_MS } from "@/services/tracking";
import { getRoutePlan } from "@/services/dispatch";
import { RoutePlanPanel } from "@/features/tracking/route-plan-panel";
import { HAS_GOOGLE_MAPS_KEY } from "@/config/site";
import { queryKeys } from "@/lib/query-keys";
import { useI18n, useT } from "@/i18n";
import { formatDateTime } from "@/lib/format";
import { friendlyErrorMessage } from "@/lib/errors";
import type { LiveCaptain } from "@/types/tracking";

// The Maps SDK touches `window`, so this cannot be server-rendered.
const FleetMap = dynamic(() => import("@/features/tracking/fleet-map").then((m) => m.FleetMap), {
  ssr: false,
  loading: () => <Skeleton className="h-[28rem] w-full rounded-lg" />,
});

/**
 * Where the fleet is.
 *
 * Two things this screen is careful about, because a tracking map is trusted more than it
 * deserves to be:
 *
 *  - **Age is shown as prominently as position.** A dot looks equally confident whether the fix
 *    is four seconds or forty minutes old. Every row and every marker is coloured by how stale
 *    it is, using the dispatch algorithm's own thresholds — so the screen and the ranking agree
 *    about what "stale" means.
 *  - **Captains with no position at all are listed**, not hidden. One who went on duty and has
 *    never reported is the most interesting row here: it means their app is not sending, and
 *    dropping them would conceal the fault instead of showing it.
 */
export default function TrackingPage() {
  const t = useT();
  const { locale } = useI18n();
  const [selected, setSelected] = useState<string | null>(null);

  const { data, isLoading, isError, error, refetch, isFetching } = useQuery({
    queryKey: queryKeys.tracking.live,
    queryFn: getLiveFleet,
    refetchInterval: LIVE_FLEET_POLL_MS,
    // Keep polling with the tab in the background: a dispatcher watching this on a second screen
    // should not come back to a frozen map that looks current.
    refetchIntervalInBackground: true,
  });

  const onSelect = useCallback((uuid: string) => setSelected(uuid), []);

  /*
   * The selected captain's route, for the map to draw.
   *
   * The same query key the panel below uses, which is the point: TanStack Query serves both from
   * one cache entry, so the map and the list can never show different versions of a route and the
   * screen makes one request, not two.
   */
  const routeQuery = useQuery({
    queryKey: queryKeys.dispatch.routePlan(selected ?? ""),
    queryFn: async () => (await getRoutePlan(selected as string)).Model ?? null,
    enabled: selected !== null,
  });

  const fleet = data?.Model ?? null;
  const captains = fleet?.captains ?? [];
  const summary = fleet?.summary ?? null;
  // Resolved from the current snapshot rather than held alongside it: a captain who goes off duty
  // between two polls disappears from the list, and their route panel goes with them.
  const selectedCaptain = captains.find((captain) => captain.uuid === selected) ?? null;

  return (
    <div>
      <PageHeader
        title={t("tracking.title")}
        subtitle={t("tracking.subtitle")}
        actions={
          fleet ? (
            <span className="flex items-center gap-2 text-xs text-muted-foreground">
              <Radio
                className={isFetching ? "h-3 w-3 animate-pulse text-success" : "h-3 w-3"}
                aria-hidden
              />
              {t("tracking.asOf")} {formatDateTime(fleet.as_of, locale)}
            </span>
          ) : null
        }
      />

      {summary ? (
        <div className="mb-4 grid grid-cols-2 gap-3 sm:grid-cols-5">
          <Tile label={t("tracking.onDuty")} value={summary.on_duty} />
          <Tile label={t("tracking.carrying")} value={summary.carrying} />
          <Tile label={t("tracking.idle")} value={summary.idle} />
          <Tile label={t("tracking.onBreak")} value={summary.on_break} />
          <Tile
            label={t("tracking.notReporting")}
            value={summary.stale + summary.never_reported}
            warn={summary.stale + summary.never_reported > 0}
          />
        </div>
      ) : null}

      {/*
        Promoted to a banner rather than left as a tile. A captain whose app stopped reporting is
        invisible on every other screen in this application — the orders still look assigned and
        the shift still looks staffed.
      */}
      {summary && summary.stale + summary.never_reported > 0 ? (
        <div className="mb-4 flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive">
          <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" aria-hidden />
          <span>{t("tracking.notReportingWarning")}</span>
        </div>
      ) : null}

      {isError ? (
        <ErrorState message={friendlyErrorMessage(error, t)} onRetry={() => refetch()} />
      ) : (
        <>
          {/*
            Mounted straight away, deliberately **not** behind `isLoading`.

            Two slow things have to happen before a dispatcher sees a map: our own response, and
            Google's SDK download. Gating the map on the response put them in a queue — the
            browser could not begin fetching ~300 KB from Google until our API had answered, so
            the wait was the sum of the two rather than the longer of them. Mounting it now runs
            them side by side, and `captains` is simply an empty array until the data lands.
          */}
          <Card className="mb-4 overflow-hidden p-2">
            {/*
              A missing key is stated, not shrugged at. Google renders a grey rectangle with the
              reason only in the browser console, which looks like our bug and sends whoever is
              looking to the wrong place. The table below still works, so the screen is useful
              either way.
            */}
            {HAS_GOOGLE_MAPS_KEY ? (
              <FleetMap
                captains={captains}
                selected={selected}
                onSelect={onSelect}
                route={routeQuery.data ?? null}
              />
            ) : (
              <div className="flex h-[28rem] w-full flex-col items-center justify-center gap-2 rounded-lg border border-dashed p-6 text-center">
                <MapPin className="h-6 w-6 text-muted-foreground" aria-hidden />
                <p className="text-sm font-medium text-foreground">{t("tracking.noMapKey")}</p>
                <p className="max-w-md text-xs text-muted-foreground">
                  {t("tracking.noMapKeyHint")}
                </p>
              </div>
            )}
          </Card>

          {/*
            The selected captain's route, between the map and the table.

            Shown only for a selection, because a route is about one captain and a list of eleven
            of them is not a route. Selecting is already how the map and the table talk to each
            other, so this reuses it rather than inventing a second idea of "current captain".
          */}
          {selectedCaptain ? (
            <div className="mb-4">
              <RoutePlanPanel
                captainUlid={selectedCaptain.uuid}
                captainName={selectedCaptain.name}
              />
            </div>
          ) : null}

          <Card>
            {isLoading ? (
              <div className="p-4">
                <TableSkeleton />
              </div>
            ) : captains.length === 0 ? (
              <EmptyState
                title={t("tracking.empty")}
                description={t("tracking.emptyHint")}
                icon={MapPin}
              />
            ) : (
              <div className="overflow-x-auto">
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead>{t("tracking.captain")}</TableHead>
                      <TableHead>{t("tracking.gps")}</TableHead>
                      <TableHead>{t("tracking.lastFix")}</TableHead>
                      <TableHead>{t("tracking.carrying")}</TableHead>
                      <TableHead>{t("tracking.vehicle")}</TableHead>
                      <TableHead aria-label={t("common.actions")} />
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {captains.map((captain) => (
                      <CaptainRow
                        key={captain.uuid}
                        captain={captain}
                        selected={selected === captain.uuid}
                        onSelect={() => setSelected(captain.uuid)}
                        locale={locale}
                        t={t as unknown as (key: string) => string}
                      />
                    ))}
                  </TableBody>
                </Table>
              </div>
            )}
          </Card>
        </>
      )}
    </div>
  );
}

function Tile({ label, value, warn }: { label: string; value: number; warn?: boolean }) {
  return (
    <Card className="p-4">
      <p className="text-xs text-muted-foreground">{label}</p>
      <p
        className={
          warn
            ? "mt-1 text-2xl font-semibold text-destructive"
            : "mt-1 text-2xl font-semibold text-foreground"
        }
      >
        {value}
      </p>
    </Card>
  );
}

function CaptainRow({
  captain,
  selected,
  onSelect,
  locale,
  t,
}: {
  captain: LiveCaptain;
  selected: boolean;
  onSelect: () => void;
  locale: "en" | "ar";
  t: (key: string) => string;
}) {
  return (
    <TableRow className={selected ? "bg-muted/50" : undefined}>
      <TableCell>
        <button type="button" onClick={onSelect} className="text-start">
          <p className="font-medium text-foreground hover:underline">{captain.name}</p>
          {captain.phone ? (
            <p className="text-xs text-muted-foreground">{captain.phone}</p>
          ) : null}
        </button>
      </TableCell>

      <TableCell>
        <div className="flex items-center gap-2">
          <StatusBadge variant={gpsTone(captain.gps_state)}>
            {t(`tracking.gps.${captain.gps_state}`)}
          </StatusBadge>
          {captain.on_break ? (
            <span className="text-[0.7rem] text-muted-foreground">{t("tracking.onBreak")}</span>
          ) : null}
        </div>
      </TableCell>

      <TableCell className="text-xs text-muted-foreground">
        {captain.captured_at ? (
          <>
            {formatDateTime(captain.captured_at, locale)}
            {/* Measured on the server's clock, not the browser's. */}
            {captain.age_seconds !== null ? (
              <p className="text-[0.7rem]">{formatAge(captain.age_seconds, t)}</p>
            ) : null}
          </>
        ) : (
          <span className="italic">{t("tracking.neverReported")}</span>
        )}
      </TableCell>

      <TableCell className="numeric">{captain.active_orders}</TableCell>

      <TableCell className="text-xs text-muted-foreground">
        {captain.vehicle?.plate_number ?? "—"}
      </TableCell>

      <TableCell>
        <div className="flex items-center gap-3">
          {/* The same selection the map uses, given a name — clicking a row is discoverable only
              once somebody has already done it. */}
          <button
            type="button"
            onClick={onSelect}
            className="text-xs font-medium text-primary hover:underline"
          >
            {t("routePlan.show")}
          </button>
          <Link
            href={`/captains/${captain.uuid}`}
            className="text-xs font-medium text-primary hover:underline"
          >
            {t("common.view")}
          </Link>
        </div>
      </TableCell>
    </TableRow>
  );
}

/** Whole units only — "2 minutes ago" is what somebody needs, not "2.4 minutes ago". */
function formatAge(seconds: number, t: (key: string) => string): string {
  if (seconds < 60) return `${seconds}${t("tracking.secondsShort")}`;

  const minutes = Math.floor(seconds / 60);
  if (minutes < 60) return `${minutes}${t("tracking.minutesShort")}`;

  return `${Math.floor(minutes / 60)}${t("tracking.hoursShort")}`;
}
