import {
  stationPasses,
  targetPasses,
  type GroundPoint,
  type PassOrbit
} from "../tgu-map-2d/geometry/passes";
import type { Draft, EditorCheckContext, EditorGroundStation, EditorWork } from "./model/editorTypes";

export type EditorConflictMap = Record<string, string[]>;

const DEFAULT_VISIBILITY_MARGIN_MS = 6 * 60 * 60 * 1000;

export function overlaps(works: EditorWork[]): EditorConflictMap {
  const result: EditorConflictMap = {};

  for (let firstIndex = 0; firstIndex < works.length; firstIndex += 1) {
    for (let secondIndex = firstIndex + 1; secondIndex < works.length; secondIndex += 1) {
      const first = works[firstIndex];
      const second = works[secondIndex];
      if (Math.max(first.startMs, second.startMs) < Math.min(first.endMs, second.endMs)) {
        addConflict(result, first.id, `Пересечение по времени с "${kindLabel(second.kind)}"`);
        addConflict(result, second.id, `Пересечение по времени с "${kindLabel(first.kind)}"`);
      }
    }
  }

  return result;
}

export function checkVisibility(draft: Draft, context: EditorCheckContext): EditorConflictMap {
  const result: EditorConflictMap = {};
  const orbit: PassOrbit = { spacecraftId: context.spacecraftId ?? draft.spacecraftId };
  const marginMs = context.marginMs ?? DEFAULT_VISIBILITY_MARGIN_MS;

  for (const work of draft.works) {
    if (work.kind === "shoot" && work.targetLat !== undefined && work.targetLon !== undefined) {
      const windows = targetPasses(orbit, { lat: work.targetLat, lon: work.targetLon }, {
        fromMs: work.startMs - marginMs,
        toMs: work.endMs + marginMs
      });
      if (!isCovered(work, windows)) {
        addConflict(result, work.id, "Съёмка вне зоны видимости цели");
      }
    }

    if (work.kind === "downlink" && work.stationId) {
      const station = context.stations.find((item) => item.id === work.stationId);
      const point = stationPoint(station);
      if (!point) {
        addConflict(result, work.id, "Нет координат станции для проверки видимости");
        continue;
      }

      const windows = stationPasses(orbit, point, {
        fromMs: work.startMs - marginMs,
        toMs: work.endMs + marginMs
      });
      if (!isCovered(work, windows)) {
        addConflict(result, work.id, "Сброс вне сеанса связи со станцией");
      }
    }
  }

  return result;
}

export function mergeConflictMaps(...maps: EditorConflictMap[]): EditorConflictMap {
  const result: EditorConflictMap = {};
  for (const map of maps) {
    for (const [workId, messages] of Object.entries(map)) {
      result[workId] = [...(result[workId] ?? []), ...messages];
    }
  }
  return result;
}

export function conflictCount(map: EditorConflictMap): number {
  return Object.keys(map).length;
}

function isCovered(work: EditorWork, windows: Array<{ startMs: number; endMs: number }>): boolean {
  return windows.some((window) => work.startMs >= window.startMs - 1 && work.endMs <= window.endMs + 1);
}

function stationPoint(station?: EditorGroundStation): GroundPoint | undefined {
  if (station?.lat === undefined || station.lon === undefined) {
    return undefined;
  }
  return { lat: station.lat, lon: station.lon };
}

export function kindLabel(kind: EditorWork["kind"]): string {
  switch (kind) {
    case "shoot":
      return "Съёмка";
    case "downlink":
      return "Сброс";
  }
}

function addConflict(map: EditorConflictMap, workId: string, message: string): void {
  map[workId] = [...(map[workId] ?? []), message];
}
