import { useCallback, useEffect, useMemo, useState } from "react";
import { fetchRequestsForMap } from "../../api/requestApi";
import { fetchStations, type StationDto } from "../../api/stationsApi";
import type { RequestMapItem } from "../../model/requestTypes";
import type { TguPlanUi, TguPlatformUi, TimelineRange } from "../../model/timelineTypes";
import { platformLabel } from "../tgu-planning/tguTimelineMapper";
import { DEFAULT_TGU_2D_MAP_LAYERS, type Tgu2DMapLayerKey, type Tgu2DMapLayersState } from "./model/mapLayerTypes";
import type { Tgu2DMapSelection } from "./model/mapSelectionTypes";
import { buildTgu2DMapScene, getLayerAvailability } from "./Tgu2DMapLayers";
import { Tgu2DMapDetails } from "./Tgu2DMapDetails";
import { MapObjectInspector } from "./MapObjectInspector";
import { Tgu2DMapSidebar } from "./Tgu2DMapSidebar";
import { Tgu2DMapToolbar } from "./Tgu2DMapToolbar";
import { Tgu2DMapView } from "./Tgu2DMapView";
import { getTgu2DMapIntervalState, TGU_2D_MAP_INTERVAL_WARNING } from "./tgu2DMapInterval";
import { parseLocal, toLocalInputValue, toUtcIso } from "../../utils/localTime";

type Tgu2DMapTabProps = {
  range: TimelineRange;
  invalidRange: boolean;
  selectedPlan?: TguPlanUi;
  platforms: TguPlatformUi[];
};

export type RequestsState = {
  fromValue: string;
  toValue: string;
  items: RequestMapItem[];
  loading: boolean;
  loadedCount: number;
  error?: string;
  totalItems: number;
  truncated: boolean;
  loaded: boolean;
};

function buildInitialRequestsState(range: TimelineRange): RequestsState {
  return {
    fromValue: toLocalInputValue(range.fromMs),
    toValue: toLocalInputValue(range.toMs),
    items: [],
    loading: false,
    loadedCount: 0,
    totalItems: 0,
    truncated: false,
    loaded: false
  };
}

export function Tgu2DMapTab({
  range,
  invalidRange,
  selectedPlan,
  platforms
}: Tgu2DMapTabProps) {
  const [layers, setLayers] = useState<Tgu2DMapLayersState>(DEFAULT_TGU_2D_MAP_LAYERS);
  const [search, setSearch] = useState("");
  const [redrawToken, setRedrawToken] = useState(0);
  const [hiddenIds, setHiddenIds] = useState<ReadonlySet<string>>(new Set());
  const [requestsState, setRequestsState] = useState<RequestsState>(() => buildInitialRequestsState(range));
  const [stations, setStations] = useState<StationDto[]>([]);
  const [stationsFetchFailed, setStationsFetchFailed] = useState(false);
  // Инспектор объекта карты справа (единый для всех вкладок): объекты под кликом и раскрытый.
  const [mapHits, setMapHits] = useState<Tgu2DMapSelection[]>([]);
  const [activeHitId, setActiveHitId] = useState<string>();

  useEffect(() => {
    fetchStations()
      .then((items) => { setStations(items); setStationsFetchFailed(false); })
      .catch(() => setStationsFetchFailed(true));
  }, []);

  const mapInterval = getTgu2DMapIntervalState(range, selectedPlan);
  const mapInvalidRange = invalidRange || mapInterval.tooLarge;

  const loadRequests = useCallback(async (fromValue: string, toValue: string) => {
    const fromMs = parseLocal(fromValue);
    const toMs = parseLocal(toValue);
    if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || fromMs >= toMs) return;

    setRequestsState((current) => ({ ...current, loading: true, error: undefined, loadedCount: 0 }));
    try {
      const result = await fetchRequestsForMap(
        {
          endFrom: toUtcIso(fromMs),
          beginTo: toUtcIso(toMs)
        },
        (loaded, total) => {
          setRequestsState((current) => ({ ...current, loadedCount: loaded, totalItems: total }));
        }
      );
      setRequestsState((current) => ({
        ...current,
        items: result.items,
        loadedCount: result.items.length,
        totalItems: result.totalItems,
        truncated: result.truncated,
        loading: false,
        loaded: true
      }));
    } catch (error) {
      setRequestsState((current) => ({
        ...current,
        loading: false,
        error: error instanceof Error ? error.message : "Не удалось загрузить заявки."
      }));
    }
  }, []);

  const applyRequestsRange = useCallback(() => {
    void loadRequests(requestsState.fromValue, requestsState.toValue);
  }, [loadRequests, requestsState.fromValue, requestsState.toValue]);

  const setRequestsFrom = useCallback((fromValue: string) => {
    setRequestsState((current) => ({ ...current, fromValue }));
  }, []);

  const setRequestsTo = useCallback((toValue: string) => {
    setRequestsState((current) => ({ ...current, toValue }));
  }, []);

  const setRequestsQuickRange = useCallback((fromMs: number, toMs: number) => {
    const fromValue = toLocalInputValue(fromMs);
    const toValue = toLocalInputValue(toMs);
    setRequestsState((current) => ({ ...current, fromValue, toValue }));
    void loadRequests(fromValue, toValue);
  }, [loadRequests]);

  useEffect(() => {
    setRequestsState(buildInitialRequestsState(range));
  }, [range]);

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

  const showAll = useCallback(() => setHiddenIds(new Set()), []);

  const planPlatform = useMemo(
    () => selectedPlan ? platforms.find((p) => p.spacecraftId === selectedPlan.spacecraftId) : undefined,
    [platforms, selectedPlan]
  );
  const satelliteLabel = selectedPlan
    ? platformLabel(planPlatform, selectedPlan.spacecraftId)
    : hiddenIds.size > 0
      ? `Скрыто: ${hiddenIds.size} КА`
      : "Все КА";

  const intervalLabel = `${formatDate(mapInterval.range.fromMs)} - ${formatDate(mapInterval.range.toMs)}`;

  const scene = useMemo(
    () => buildTgu2DMapScene({
      range: mapInterval.range,
      selectedPlan,
      platforms,
      hiddenSpacecraftIds: hiddenIds,
      requestItems: layers.requests ? requestsState.items : [],
      stationItems: layers.stations ? stations : []
    }),
    [mapInterval.range, platforms, selectedPlan, hiddenIds, requestsState.items, layers.requests, stations, layers.stations]
  );
  const layerAvailability = useMemo(
    () => getLayerAvailability(layers, scene, stationsFetchFailed),
    [layers, scene, stationsFetchFailed]
  );

  const overlayMessage = invalidRange
    ? "Некорректный интервал: from должен быть меньше to."
    : mapInterval.tooLarge
      ? TGU_2D_MAP_INTERVAL_WARNING
      : undefined;

  const toggleLayer = (key: Tgu2DMapLayerKey) => {
    setLayers((current) => ({ ...current, [key]: !current[key] }));
  };

  // Клик по карте: 0/1 объект — карточка сразу; ≥2 (наложение) — список под точкой клика.
  const handleObjectSelect = useCallback((selection?: Tgu2DMapSelection) => {
    setMapHits(selection ? [selection] : []);
    setActiveHitId(selection?.id);
  }, []);

  const handleObjectsAtPoint = useCallback((selections: Tgu2DMapSelection[]) => {
    setMapHits(selections);
    setActiveHitId(selections[0]?.id);
  }, []);

  const toggleHit = useCallback((selection: Tgu2DMapSelection) => {
    setActiveHitId((current) => (current === selection.id ? undefined : selection.id));
  }, []);

  const closeInfoPanel = useCallback(() => {
    setMapHits([]);
    setActiveHitId(undefined);
  }, []);

  return (
    <main className="tgu-map-workspace">
      <Tgu2DMapToolbar
        satelliteLabel={satelliteLabel}
        intervalLabel={intervalLabel}
        layers={layers}
        onRefresh={() => setRedrawToken((value) => value + 1)}
        onToggleLayer={toggleLayer}
      />

      <div className={`tgu-map-content${mapHits.length > 0 ? " has-side-panel" : ""}`}>
        <Tgu2DMapSidebar
          platforms={platforms}
          selectedPlanId={selectedPlan?.planId}
          hiddenIds={hiddenIds}
          layers={layers}
          search={search}
          layerAvailability={layerAvailability}
          requestsState={requestsState}
          onSearchChange={setSearch}
          onToggleSpacecraft={toggleSpacecraft}
          onShowAll={showAll}
          onToggleLayer={toggleLayer}
          onRequestsFromChange={setRequestsFrom}
          onRequestsToChange={setRequestsTo}
          onRequestsApply={applyRequestsRange}
          onRequestsQuickRange={setRequestsQuickRange}
        />
        <section className="tgu-map-stage">
          {overlayMessage && <div className="tgu-map-overlay">{overlayMessage}</div>}
          <Tgu2DMapView
            scene={mapInvalidRange ? { polygons: [], lines: [], markers: [] } : scene}
            layers={layers}
            redrawToken={redrawToken}
            enableObjectChooser
            onObjectsAtPoint={handleObjectsAtPoint}
            onObjectSelect={handleObjectSelect}
            selectedObjectId={activeHitId}
          />
        </section>
        {mapHits.length > 0 && (
          <aside className="tgu-editor-side-panel">
            <MapObjectInspector
              hits={mapHits}
              activeId={activeHitId}
              onToggle={toggleHit}
              onClose={closeInfoPanel}
              requestItems={requestsState.items}
            />
          </aside>
        )}
      </div>

      <div className="tgu-map-errors">
        {Object.entries(layerAvailability).map(([layer, message]) =>
          message ? (
            <div className="tgu-alert tgu-alert--warning" key={layer}>
              {layerLabel(layer as Tgu2DMapLayerKey)}: {message}
            </div>
          ) : null
        )}
      </div>

      <Tgu2DMapDetails selectedPlan={selectedPlan} />
    </main>
  );
}

function formatDate(ms: number): string {
  return new Date(ms).toLocaleString("ru-RU", {
    day: "2-digit",
    month: "2-digit",
    hour: "2-digit",
    minute: "2-digit"
  });
}

function layerLabel(layer: Tgu2DMapLayerKey): string {
  switch (layer) {
    case "tracks":
      return "Трасса КА";
    case "swath":
      return "Полоса обзора";
    case "planWorks":
      return "Работы плана";
    case "complexPlan":
      return "Комплексный план";
    case "heatmap":
      return "Тепловая карта";
    case "stations":
      return "Станции";
    case "spacecraftMarkers":
      return "Маркеры КА";
    case "requests":
      return "Заявки на съёмку";
  }
}
