import { useCallback, useEffect, useMemo, useState } from "react";
import { fetchRequestsForMap } from "../../api/requestApi";
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 { Tgu2DMapSidebar } from "./Tgu2DMapSidebar";
import { Tgu2DMapToolbar } from "./Tgu2DMapToolbar";
import { Tgu2DMapView } from "./Tgu2DMapView";
import { getTgu2DMapIntervalState, TGU_2D_MAP_INTERVAL_WARNING } from "./tgu2DMapInterval";

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: formatDateTimeLocal(range.fromMs),
    toValue: formatDateTimeLocal(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 [selectedObject, setSelectedObject] = useState<Tgu2DMapSelection>();
  const [hiddenIds, setHiddenIds] = useState<ReadonlySet<string>>(new Set());
  const [requestsState, setRequestsState] = useState<RequestsState>(() => buildInitialRequestsState(range));

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

  const loadRequests = useCallback(async (fromValue: string, toValue: string) => {
    const fromMs = new Date(fromValue).getTime();
    const toMs = new Date(toValue).getTime();
    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: new Date(fromMs).toISOString(),
          beginTo: new Date(toMs).toISOString()
        },
        (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 = formatDateTimeLocal(fromMs);
    const toValue = formatDateTimeLocal(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 : []
    }),
    [mapInterval.range, platforms, selectedPlan, hiddenIds, requestsState.items, layers.requests]
  );
  const layerAvailability = useMemo(() => getLayerAvailability(layers, scene), [layers, scene]);

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

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

  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">
        <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}
            onObjectSelect={setSelectedObject}
          />
        </section>
      </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} selectedObject={selectedObject} />
    </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 formatDateTimeLocal(ms: number): string {
  const date = new Date(ms);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  return `${year}-${month}-${day}T${hours}:${minutes}`;
}

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