import { useEffect, useMemo, useRef, useState, useCallback, type MutableRefObject } from "react";
import { fetchRequestsForMap } from "../../api/requestApi";
import { fetchFlightLine, type FlightLinePoint } from "../../api/tguApi";
import type { RequestMapItem } from "../../model/requestTypes";
import { stationPasses, targetPasses, type OrbitPassInfo, type PassRange } from "../tgu-map-2d/geometry/passes";
import { Tgu2DMapView } from "../tgu-map-2d/Tgu2DMapView";
import { parseWktPolygon } from "../tgu-map-2d/geometry/wktParser";
import type { Tgu2DMapLayersState } from "../tgu-map-2d/model/mapLayerTypes";
import type { MapLine, MapPolygon, Tgu2DMapScene } from "../tgu-map-2d/model/mapTypes";
import { fetchPlanModes, saveEditedPlan, type PlanMode } from "./editorApi";
import {
  addDownlink,
  addShoot,
  deleteWork,
  duplicateWork,
  mutate,
  redo,
  seedDraft,
  undo,
  updateWork
} from "./editorDraft";
import { checkVisibility, conflictCount, mergeConflictMaps, overlaps, type EditorConflictMap } from "./editorConflicts";
import { DEFAULT_EDITOR_MODES, formatDuration } from "./editorPresentation";
import { DownlinkPanel, type DownlinkSelection, type DownlinkStationOption, ShootPanel, WorkInspector } from "./EditorPanels";
import { MapPassSelector } from "./MapPassSelector";
import { EditorRail } from "./EditorRail";
import { EditorTimeline } from "./EditorTimeline";
import { EditorToolbar } from "./EditorToolbar";
import type {
  DraftHistory,
  EditorGroundStation,
  EditorMode,
  EditorShootWork,
  EditorTarget,
  EditorTimelineWindow,
  EditorVisibilityTrack,
  EditorVisibilityWindow,
  EditorWorkPatch,
  TguEditorTabProps
} from "./model/editorTypes";

type EditorRightMode = "none" | "inspect" | "shoot" | "downlink";

const HOUR_MS = 60 * 60 * 1000;
const EDITOR_MAP_LAYERS: Tgu2DMapLayersState = {
  tracks: true,
  swath: true,
  planWorks: true,
  stations: true,
  spacecraftMarkers: false,
  requests: true
};

export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange, platforms }: TguEditorTabProps) {
  const idCounterRef = useRef(0);
  const targetCounterRef = useRef(0);
  const centerRef = useRef<HTMLElement>(null);
  const [mapRatio, setMapRatio] = useState(0.5);
  const [history, setHistory] = useState<DraftHistory>(() => ({
    draft: seedDraft(selectedPlan, selectedSpacecraftId),
    past: [],
    future: []
  }));
  const [window, setWindow] = useState<EditorTimelineWindow>(() => defaultEditorWindow(selectedPlan, appliedRange));
  const [rightMode, setRightMode] = useState<EditorRightMode>("none");
  const [selectedWorkId, setSelectedWorkId] = useState<string>();
  const [target, setTarget] = useState<EditorTarget>();
  const [selectedShootPass, setSelectedShootPass] = useState<EditorVisibilityWindow>();
  const [modeId, setModeId] = useState(DEFAULT_EDITOR_MODES[0].id);
  const [selectedDownlink, setSelectedDownlink] = useState<DownlinkSelection>();
  const [serviceConflicts, setServiceConflicts] = useState<EditorConflictMap>({});
  const [checking, setChecking] = useState(false);
  const [applying, setApplying] = useState(false);
  const [notice, setNotice] = useState<string>();
  const [applyError, setApplyError] = useState<string>();
  const [contextSpacecraftIds, setContextSpacecraftIds] = useState<Set<string>>(new Set());
  const [requestItems, setRequestItems] = useState<RequestMapItem[]>([]);
  const [planModes, setPlanModes] = useState<PlanMode[]>([]);
  const [passFrom, setPassFrom] = useState(1);
  const [passTo, setPassTo] = useState(999);
  const [flightLineData, setFlightLineData] = useState<FlightLinePoint[]>([]);
  const draft = history.draft;
  const stations = useMemo(() => buildStations(selectedPlan), [selectedPlan]);
  const mapRange = useMemo<PassRange>(() => {
    const center = (window.fromMs + window.toMs) / 2;
    return { fromMs: center - MAP_TRACK_WINDOW_MS / 2, toMs: center + MAP_TRACK_WINDOW_MS / 2 };
  }, [window.fromMs, window.toMs]);
  const noradId = useMemo(() => {
    if (!selectedSpacecraftId) return undefined;
    const platform = platforms.find((p) => p.spacecraftId === selectedSpacecraftId);
    return platform?.noradId ?? undefined;
  }, [selectedSpacecraftId, platforms]);
  const orbitPasses = useMemo<OrbitPassInfo[]>(() => {
    if (flightLineData.length === 0) return [];
    const groups = new Map<number, FlightLinePoint[]>();
    for (const pt of flightLineData) {
      const arr = groups.get(pt.revolution) ?? [];
      arr.push(pt);
      groups.set(pt.revolution, arr);
    }
    return Array.from(groups.entries())
      .sort(([a], [b]) => a - b)
      .map(([revolution, pts], idx) => ({
        index: idx + 1,
        revolution,
        startMs: Date.parse(pts[0].time),
        endMs: Date.parse(pts[pts.length - 1].time),
      }));
  }, [flightLineData]);
  const modes: EditorMode[] = DEFAULT_EDITOR_MODES;
  const liveConflicts = useMemo(() => overlaps(draft.works), [draft.works]);
  const conflicts = useMemo(() => mergeConflictMaps(liveConflicts, serviceConflicts), [liveConflicts, serviceConflicts]);
  const selectedWork = draft.works.find((work) => work.id === selectedWorkId);
  const activeLabel = platformLabel(platforms, selectedSpacecraftId);
  const shootPasses = useMemo(
    () => target ? targetPasses({ spacecraftId: selectedSpacecraftId }, target, { fromMs: window.fromMs, toMs: window.toMs }) : [],
    [selectedSpacecraftId, target, window.fromMs, window.toMs]
  );
  const stationOptions = useMemo(
    () => buildStationOptions(stations, selectedSpacecraftId, window, selectedPlan),
    [selectedPlan, selectedSpacecraftId, stations, window]
  );
  const visibilityTracks = useMemo(
    () => buildVisibilityTracks(target, shootPasses, selectedWork, stations, selectedSpacecraftId, window),
    [selectedSpacecraftId, selectedWork, shootPasses, stations, target, window]
  );
  const effectiveFrom = Math.max(1, Math.min(passFrom, orbitPasses.length || 1));
  const effectiveTo = Math.max(effectiveFrom, Math.min(passTo, orbitPasses.length || 1));
  const mapScene = useMemo(
    () => buildEditorMapScene(selectedSpacecraftId, flightLineData, orbitPasses, stations, requestItems, planModes, effectiveFrom, effectiveTo),
    [selectedSpacecraftId, flightLineData, orbitPasses, stations, requestItems, planModes, effectiveFrom, effectiveTo]
  );
  const shootTargets = useMemo(
    () =>
      draft.works
        .filter((w): w is EditorShootWork => w.kind === "shoot" && w.targetLat !== undefined && w.targetLon !== undefined)
        .map((w) => ({ id: w.id, name: w.label, lat: w.targetLat!, lon: w.targetLon! })),
    [draft.works]
  );
  const contextLanes = useMemo(
    () =>
      Array.from(contextSpacecraftIds).map((spacecraftId) => ({
        spacecraftId,
        label: platformLabel(platforms, spacecraftId),
        works: []
      })),
    [contextSpacecraftIds, platforms]
  );
  const dirty = history.past.length > 0 || draft.works.some((work) => work.isNew);
  const addedCount = draft.works.filter((work) => work.isNew).length;

  useEffect(() => {
    setHistory({ draft: seedDraft(selectedPlan, selectedSpacecraftId), past: [], future: [] });
    setWindow(defaultEditorWindow(selectedPlan, appliedRange));
    setSelectedWorkId(undefined);
    setRightMode("none");
    setTarget(undefined);
    setSelectedShootPass(undefined);
    setSelectedDownlink(undefined);
    setServiceConflicts({});
    setNotice(undefined);
    setApplyError(undefined);
    setPlanModes([]);
    setPassFrom(1);
    setPassTo(999);
    setFlightLineData([]);
  }, [appliedRange, selectedPlan, selectedSpacecraftId]);

  useEffect(() => {
    if (!noradId) return;
    let cancelled = false;
    void fetchFlightLine(noradId, mapRange.fromMs, mapRange.toMs)
      .then((data) => { if (!cancelled) setFlightLineData(data); })
      .catch(() => { if (!cancelled) setFlightLineData([]); });
    return () => { cancelled = true; };
  }, [noradId, mapRange]);

  useEffect(() => {
    const controller = new AbortController();
    void fetchRequestsForMap({
      endFrom: new Date(window.fromMs).toISOString(),
      beginTo: new Date(window.toMs).toISOString()
    }).then((result) => {
      if (!controller.signal.aborted) {
        setRequestItems(result.items);
      }
    }).catch(() => {
      // silently ignore — requests are supplementary
    });
    return () => controller.abort();
  }, [window.fromMs, window.toMs]);

  useEffect(() => {
    if (!selectedPlan) return;
    const planId = selectedPlan.planId;
    let cancelled = false;
    void fetchPlanModes(planId).then((modes) => {
      if (!cancelled) setPlanModes(modes);
    }).catch(() => {
      // silently ignore — plan modes are supplementary display data
    });
    return () => { cancelled = true; };
  }, [selectedPlan]);

  const changeDraft = (transform: (draft: DraftHistory["draft"]) => DraftHistory["draft"]) => {
    setHistory((current) => mutate(current, transform));
    setServiceConflicts({});
    setNotice(undefined);
    setApplyError(undefined);
  };

  const onChangeWork = (workId: string, patch: EditorWorkPatch) => {
    changeDraft((current) => updateWork(current, workId, patch));
  };

  const addShootWork = () => {
    if (!target || !selectedShootPass || !modeId) return;

    const durationMs = Math.min(selectedShootPass.endMs - selectedShootPass.startMs, 30 * 60 * 1000);
    const startMs = selectedShootPass.startMs + Math.floor((selectedShootPass.endMs - selectedShootPass.startMs - durationMs) / 2);
    const mode = modes.find((item) => item.id === modeId);
    const id = nextWorkId(idCounterRef);

    changeDraft((current) =>
      addShoot(current, {
        id,
        startMs,
        endMs: startMs + durationMs,
        label: mode?.label ?? "Съёмка",
        modeId,
        target
      })
    );
    setSelectedWorkId(id);
    setRightMode("inspect");
    setTarget(undefined);
    setSelectedShootPass(undefined);
  };

  const addDownlinkWork = () => {
    if (!selectedDownlink) return;

    const durationMs = Math.min(selectedDownlink.pass.endMs - selectedDownlink.pass.startMs, 12 * 60 * 1000);
    const startMs = selectedDownlink.pass.startMs + Math.floor((selectedDownlink.pass.endMs - selectedDownlink.pass.startMs - durationMs) / 2);
    const id = nextWorkId(idCounterRef);

    changeDraft((current) =>
      addDownlink(current, {
        id,
        startMs,
        endMs: startMs + durationMs,
        label: "Сброс на НКПОР",
        station: selectedDownlink.station
      })
    );
    setSelectedWorkId(id);
    setRightMode("inspect");
    setSelectedDownlink(undefined);
  };

  const runCheck = () => {
    setChecking(true);
    setServiceConflicts(checkVisibility(draft, { spacecraftId: selectedSpacecraftId, stations }));
    setChecking(false);
    setNotice("Проверка видимости выполнена для текущего черновика.");
  };

  const applyDraft = async () => {
    const planId = selectedPlan?.planId ?? draft.planId;
    if (!planId) {
      setApplyError("Нельзя применить черновик: план не выбран.");
      setNotice(undefined);
      return;
    }

    setApplying(true);
    setApplyError(undefined);
    setNotice(undefined);
    try {
      const result = await saveEditedPlan({ planId, draft });
      setNotice(result.message);
      setHistory((current) => ({ ...current, past: [], future: [] }));
    } catch (error) {
      setApplyError(error instanceof Error ? error.message : "Не удалось применить правки плана.");
    } finally {
      setApplying(false);
    }
  };

  const zoom = (factor: number) => {
    setWindow((current) => {
      const center = (current.fromMs + current.toMs) / 2;
      const half = ((current.toMs - current.fromMs) / 2) * factor;
      return { fromMs: center - half, toMs: center + half };
    });
  };

  const pan = (factor: number) => {
    setWindow((current) => {
      const delta = (current.toMs - current.fromMs) * factor;
      return { fromMs: current.fromMs + delta, toMs: current.toMs + delta };
    });
  };

  const handleResizerDown = useCallback((e: React.MouseEvent) => {
    e.preventDefault();
    const startY = e.clientY;
    const startRatio = mapRatio;

    const onMove = (ev: MouseEvent) => {
      const el = centerRef.current;
      if (!el) return;
      const dy = ev.clientY - startY;
      setMapRatio(Math.max(0.15, Math.min(0.80, startRatio + dy / el.clientHeight)));
    };

    const onUp = () => {
      document.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseup", onUp);
    };

    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
  }, [mapRatio]);

  if (!selectedSpacecraftId && !selectedPlan) {
    return (
      <main className="tgu-editor-workspace">
        <div className="tgu-empty">Выберите КА или план, чтобы открыть редактор включений.</div>
      </main>
    );
  }

  return (
    <main className="tgu-editor-workspace">
      <EditorToolbar
        selectedSpacecraftId={selectedSpacecraftId}
        selectedPlan={selectedPlan}
        platforms={platforms}
        canUndo={history.past.length > 0}
        canRedo={history.future.length > 0}
        checking={checking}
        conflictCount={conflictCount(conflicts)}
        addedCount={addedCount}
        dirty={dirty}
        applying={applying}
        windowLabel={formatDuration(window.toMs - window.fromMs)}
        rightMode={rightMode}
        onSetRightMode={(mode) => {
          setRightMode(mode);
          setSelectedWorkId(undefined);
          setNotice(undefined);
        }}
        onUndo={() => setHistory((current) => undo(current))}
        onRedo={() => setHistory((current) => redo(current))}
        onCheck={runCheck}
        onApply={applyDraft}
        onZoom={zoom}
        onPan={pan}
      />

      <div className="tgu-editor-notices">
        {notice && <div className="tgu-alert tgu-alert--success tgu-editor-notice">{notice}</div>}
        {applyError && <div className="tgu-alert tgu-alert--danger tgu-editor-notice">{applyError}</div>}
      </div>

      <div className="tgu-editor-layout" style={{ gridTemplateColumns: rightMode === "none" ? "15.5rem minmax(0, 1fr) 0" : "15.5rem minmax(0, 1fr) 21rem" }}>
        <EditorRail
          selectedSpacecraftId={selectedSpacecraftId}
          selectedPlan={selectedPlan}
          platforms={platforms}
          draft={draft}
          conflictCount={conflictCount(conflicts)}
          contextSpacecraftIds={contextSpacecraftIds}
          onToggleContext={(spacecraftId) => {
            setContextSpacecraftIds((current) => {
              const next = new Set(current);
              if (next.has(spacecraftId)) {
                next.delete(spacecraftId);
              } else {
                next.add(spacecraftId);
              }
              return next;
            });
          }}
        />

        <section
          className="tgu-editor-center"
          ref={centerRef}
          style={{ gridTemplateRows: `${(mapRatio * 100).toFixed(1)}% minmax(0, 1fr)` }}
        >
          <div className="tgu-editor-map-stage">
            {rightMode === "shoot" && <div className="tgu-map-overlay">Кликните точку съёмки на карте.</div>}
            {orbitPasses.length > 0 && (
              <MapPassSelector
                passes={orbitPasses}
                fromIndex={effectiveFrom}
                toIndex={effectiveTo}
                onFromChange={setPassFrom}
                onToChange={setPassTo}
              />
            )}
            <Tgu2DMapView
              scene={mapScene}
              layers={EDITOR_MAP_LAYERS}
              redrawToken={0}
              pickMode={rightMode === "shoot"}
              targets={shootTargets}
              pickTarget={target ?? undefined}
              selectedTargetId={selectedWork?.kind === "shoot" ? selectedWorkId : undefined}
              onTargetSelect={(id) => {
                setSelectedWorkId(id);
                setRightMode("inspect");
                setNotice(undefined);
              }}
              onPickPoint={(point) => {
                targetCounterRef.current += 1;
                setTarget({
                  id: `target-${targetCounterRef.current}`,
                  name: `Цель ${targetCounterRef.current}`,
                  lat: point.lat,
                  lon: point.lon
                });
                setSelectedShootPass(undefined);
                setModeId(DEFAULT_EDITOR_MODES[0].id);
                setNotice(undefined);
              }}
              onObjectSelect={() => undefined}
            />
            <div className="tgu-editor-map-resizer" onMouseDown={handleResizerDown} />
          </div>
          <EditorTimeline
            activeLabel={activeLabel}
            draft={draft.works}
            selectedWorkId={selectedWorkId}
            window={window}
            nowMs={Date.now()}
            conflicts={conflicts}
            contextLanes={contextLanes}
            visibilityTracks={visibilityTracks}
            onSelectWork={(workId) => {
              setSelectedWorkId(workId);
              setRightMode("inspect");
              setNotice(undefined);
            }}
            onClearSelection={() => {
              if (rightMode === "inspect") {
                setSelectedWorkId(undefined);
                setRightMode("none");
              }
            }}
            onChangeWork={onChangeWork}
          />
        </section>

        {rightMode !== "none" && (
          <aside className="tgu-editor-side-panel">
            {rightMode === "inspect" && selectedWork && (
              <WorkInspector
                work={selectedWork}
                modes={modes}
                stations={stations}
                conflicts={conflicts[selectedWork.id] ?? []}
                onUpdate={onChangeWork}
                onDelete={(workId) => {
                  changeDraft((current) => deleteWork(current, workId));
                  setSelectedWorkId(undefined);
                  setRightMode("none");
                }}
                onDuplicate={(workId) => {
                  const id = nextWorkId(idCounterRef);
                  changeDraft((current) => duplicateWork(current, workId, id));
                  setSelectedWorkId(id);
                }}
                onClose={() => {
                  setSelectedWorkId(undefined);
                  setRightMode("none");
                }}
              />
            )}
            {rightMode === "inspect" && !selectedWork && <div className="tgu-empty tgu-empty--compact">Включение не выбрано.</div>}
            {rightMode === "shoot" && (
              <ShootPanel
                target={target}
                passes={shootPasses}
                selectedPass={selectedShootPass}
                modeId={modeId}
                modes={modes}
                onSelectPass={setSelectedShootPass}
                onModeChange={setModeId}
                onAdd={addShootWork}
                onClose={() => {
                  setRightMode("none");
                  setTarget(undefined);
                  setSelectedShootPass(undefined);
                }}
                onClearTarget={() => {
                  setTarget(undefined);
                  setSelectedShootPass(undefined);
                }}
              />
            )}
            {rightMode === "downlink" && (
              <DownlinkPanel
                stationOptions={stationOptions}
                selected={selectedDownlink}
                onSelect={setSelectedDownlink}
                onAdd={addDownlinkWork}
                onClose={() => {
                  setRightMode("none");
                  setSelectedDownlink(undefined);
                }}
              />
            )}
          </aside>
        )}
      </div>
    </main>
  );
}

function defaultEditorWindow(
  selectedPlan: TguEditorTabProps["selectedPlan"],
  appliedRange: TguEditorTabProps["appliedRange"]
): EditorTimelineWindow {
  if (selectedPlan) {
    return {
      fromMs: selectedPlan.startMs - 3 * HOUR_MS,
      toMs: selectedPlan.endMs + 3 * HOUR_MS
    };
  }

  return { fromMs: appliedRange.fromMs, toMs: appliedRange.toMs };
}

function buildStations(selectedPlan: TguEditorTabProps["selectedPlan"]): EditorGroundStation[] {
  if (!selectedPlan?.kppId) {
    return [];
  }

  return [{ id: selectedPlan.kppId, name: selectedPlan.kppId }];
}

function buildStationOptions(
  stations: EditorGroundStation[],
  spacecraftId: string | undefined,
  window: EditorTimelineWindow,
  selectedPlan: TguEditorTabProps["selectedPlan"]
): DownlinkStationOption[] {
  return stations.map((station) => {
    const computedPasses =
      station.lat !== undefined && station.lon !== undefined
        ? stationPasses({ spacecraftId }, { lat: station.lat, lon: station.lon }, { fromMs: window.fromMs, toMs: window.toMs })
        : [];
    const fallbackPass = selectedPlan && selectedPlan.kppId === station.id
      ? [{
        startMs: Math.max(window.fromMs, selectedPlan.startMs),
        endMs: Math.min(window.toMs, selectedPlan.endMs),
        quality: 0
      }]
      : [];

    return {
      station,
      passes: computedPasses.length > 0 ? computedPasses : fallbackPass.filter((pass) => pass.endMs > pass.startMs)
    };
  });
}

function buildVisibilityTracks(
  target: EditorTarget | undefined,
  shootPasses: EditorVisibilityWindow[],
  selectedWork: DraftHistory["draft"]["works"][number] | undefined,
  stations: EditorGroundStation[],
  spacecraftId: string | undefined,
  window: EditorTimelineWindow
): EditorVisibilityTrack[] {
  const tracks: EditorVisibilityTrack[] = [];
  if (target) {
    tracks.push({
      id: "target",
      label: target.name,
      kind: "target",
      color: "var(--now)",
      windows: shootPasses
    });
  }

  if (selectedWork?.kind === "downlink" && selectedWork.stationId) {
    const station = stations.find((item) => item.id === selectedWork.stationId);
    if (station?.lat !== undefined && station.lon !== undefined) {
      tracks.push({
        id: "station",
        label: station.name,
        kind: "station",
        color: "var(--accent)",
        windows: stationPasses({ spacecraftId }, { lat: station.lat, lon: station.lon }, { fromMs: window.fromMs, toMs: window.toMs })
      });
    }
  }

  return tracks;
}

const MAP_TRACK_WINDOW_MS = 24 * 60 * 60 * 1000;

function buildEditorMapScene(
  spacecraftId: string | undefined,
  flightLineData: FlightLinePoint[],
  orbitPasses: OrbitPassInfo[],
  stations: EditorGroundStation[],
  requestItems: RequestMapItem[],
  planModes: PlanMode[],
  fromIndex: number,
  toIndex: number
): Tgu2DMapScene {
  const visibleRevolutions = new Set(
    orbitPasses.filter((p) => p.index >= fromIndex && p.index <= toIndex).map((p) => p.revolution!)
  );

  const byRevolution = new Map<number, FlightLinePoint[]>();
  for (const pt of flightLineData) {
    if (!visibleRevolutions.has(pt.revolution)) continue;
    const arr = byRevolution.get(pt.revolution) ?? [];
    arr.push(pt);
    byRevolution.set(pt.revolution, arr);
  }

  const lines: MapLine[] = [];
  const polygons: MapPolygon[] = [];
  let zIndex = 0;

  for (const [revolution, pts] of byRevolution) {
    const passIdx = orbitPasses.find((p) => p.revolution === revolution)?.index ?? 0;

    lines.push({
      id: `${spacecraftId}:track:${revolution}`,
      layer: "tracks",
      points: pts.map((p) => ({ lat: p.lat, lon: p.long })),
      spacecraftId,
      passIndex: passIdx,
      zIndex: zIndex++
    });

    const leftEdge = pts.map((p) => ({ lat: p.latLeft, lon: p.longLeft }));
    const rightEdge = [...pts].reverse().map((p) => ({ lat: p.latRight, lon: p.longRight }));
    polygons.push({
      id: `${spacecraftId}:swath:${revolution}`,
      name: `Полоса обзора витка ${revolution}`,
      kind: "swath",
      layer: "swath",
      points: [...leftEdge, ...rightEdge],
      zIndex: zIndex++
    });
  }

  for (const mode of planModes) {
    if (mode.type !== "SURVEY" || !mode.contourWkt) continue;
    const rings = parseWktPolygon(mode.contourWkt);
    for (const ring of rings) {
      if (ring.length < 3) continue;
      polygons.push({
        id: `planWork:${mode.id}:${zIndex}`,
        name: `Съёмка витка ${mode.revolution}`,
        kind: "planWork",
        layer: "planWorks",
        points: ring,
        status: "PLANNED",
        zIndex: zIndex++
      });
    }
  }

  for (const item of requestItems) {
    const rings = parseWktPolygon(item.geometry);
    for (const ring of rings) {
      if (ring.length < 3) continue;
      polygons.push({
        id: `request:${item.id}:${zIndex}`,
        name: item.name,
        kind: "request",
        layer: "requests",
        points: ring,
        requestId: item.id,
        surveyType: item.surveyType,
        zIndex: zIndex++
      });
    }
  }

  const markers = stations.flatMap((station) => {
    if (station.lat === undefined || station.lon === undefined) return [];
    return [{
      id: station.id,
      layer: "stations" as const,
      point: { lat: station.lat, lon: station.lon },
      label: station.name,
      zIndex: 10
    }];
  });

  return { polygons, lines, markers };
}

function platformLabel(platforms: TguEditorTabProps["platforms"], spacecraftId?: string): string {
  if (!spacecraftId) {
    return "КА не выбран";
  }
  const platform = platforms.find((item) => item.spacecraftId === spacecraftId);
  return platform?.name ?? platform?.noradId ?? spacecraftId;
}

function nextWorkId(ref: MutableRefObject<number>): string {
  ref.current += 1;
  return `NW-${String(ref.current).padStart(4, "0")}`;
}
