import { describe, expect, it } from "vitest";
import type { TguPlatform, TguPlanStatus } from "../../model/tguTypes";
import type { TguPlanUi } from "../../model/timelineTypes";
import {
  isOperationalPlatform,
  operationalSpacecraftIds,
  pausedSpacecraftIds,
  unresolvedProblemSpacecraftIds
} from "./tguTimelineMapper";
import { toLocalIso } from "../../utils/localTime";

function platform(overrides: Partial<TguPlatform>): TguPlatform {
  return { id: "x", noradId: 1, status: "OPERATIONAL", ...overrides };
}

function uiPlan(spacecraftId: string, status: TguPlanStatus, startMs: number): TguPlanUi {
  return {
    planId: `${spacecraftId}-${status}-${startMs}`,
    spacecraftId,
    startTime: toLocalIso(startMs),
    endTime: toLocalIso(startMs + 3_600_000),
    kppId: "KPP-1",
    status,
    startMs,
    endMs: startMs + 3_600_000
  };
}

describe("isOperationalPlatform", () => {
  it("принимает только статус OPERATIONAL без учёта регистра и пробелов", () => {
    expect(isOperationalPlatform(platform({ status: "OPERATIONAL" }))).toBe(true);
    expect(isOperationalPlatform(platform({ status: " operational " }))).toBe(true);
    expect(isOperationalPlatform(platform({ status: "STANDBY" }))).toBe(false);
    expect(isOperationalPlatform(platform({ status: "LOST" }))).toBe(false);
    expect(isOperationalPlatform(platform({ status: null }))).toBe(false);
    expect(isOperationalPlatform(platform({ status: undefined }))).toBe(false);
  });
});

describe("operationalSpacecraftIds", () => {
  it("собирает norad-id в виде строк и пропускает платформы без norad-id", () => {
    const ids = operationalSpacecraftIds([
      platform({ noradId: 2 }),
      platform({ noradId: 30 }),
      platform({ noradId: null }),
      platform({ noradId: undefined })
    ]);
    expect(ids).toEqual(new Set(["2", "30"]));
  });
});

describe("pausedSpacecraftIds", () => {
  const t0 = Date.UTC(2026, 5, 4, 0, 0, 0);
  const hour = 3_600_000;

  it("помечает КА паузой, если есть REJECTED и нет продвинувшего цепочку плана после него", () => {
    const paused = pausedSpacecraftIds([
      uiPlan("100", "REJECTED", t0),
      // проекция и черновик паузу не снимают
      uiPlan("100", "PLANNED", t0 + 2 * hour),
      uiPlan("100", "DRAFT", t0)
    ]);
    expect(paused).toEqual(new Set(["100"]));
  });

  it("снимает паузу, если оператор отправил исправленный план (в полёте) на том же/более позднем окне", () => {
    const paused = pausedSpacecraftIds([
      uiPlan("100", "REJECTED", t0),
      uiPlan("100", "WAITING_DECISION", t0)
    ]);
    expect(paused.has("100")).toBe(false);
  });

  it("не помечает паузой, если цепочка ушла вперёд (ACCEPTED после отказа)", () => {
    const paused = pausedSpacecraftIds([
      uiPlan("100", "REJECTED", t0),
      uiPlan("100", "ACCEPTED", t0 + 5 * hour)
    ]);
    expect(paused.has("100")).toBe(false);
  });

  it("без REJECTED никого не помечает", () => {
    expect(pausedSpacecraftIds([uiPlan("100", "WAITING_DECISION", t0)])).toEqual(new Set());
  });
});

describe("unresolvedProblemSpacecraftIds (красный индикатор КА)", () => {
  const t0 = Date.UTC(2026, 5, 4, 0, 0, 0);
  const hour = 3_600_000;

  function dismissed(plan: TguPlanUi): TguPlanUi {
    return { ...plan, dismissedAt: toLocalIso(t0) };
  }

  it("красит КА, если есть нерешённый REJECTED", () => {
    expect(unresolvedProblemSpacecraftIds([uiPlan("100", "REJECTED", t0)])).toEqual(new Set(["100"]));
  });

  it("красит КА на START_AMBIGUOUS", () => {
    expect(unresolvedProblemSpacecraftIds([uiPlan("100", "START_AMBIGUOUS", t0)])).toEqual(new Set(["100"]));
  });

  it("НЕ красит, если проблемный план снят с учёта", () => {
    expect(unresolvedProblemSpacecraftIds([dismissed(uiPlan("100", "REJECTED", t0))])).toEqual(new Set());
  });

  it("НЕ красит, если цепочка продвинулась за отказ (исправленный план в полёте)", () => {
    const ids = unresolvedProblemSpacecraftIds([
      uiPlan("100", "REJECTED", t0),
      uiPlan("100", "WAITING_DECISION", t0 + hour)
    ]);
    expect(ids.has("100")).toBe(false);
  });

  it("красит, если продвинувший план раньше отказа (отказ всё ещё актуален)", () => {
    const ids = unresolvedProblemSpacecraftIds([
      uiPlan("100", "REJECTED", t0 + 5 * hour),
      uiPlan("100", "AWAITING_LAYIN", t0)
    ]);
    expect(ids).toEqual(new Set(["100"]));
  });

  it("успешные/живые статусы сами по себе не красят", () => {
    expect(unresolvedProblemSpacecraftIds([uiPlan("100", "LAID_IN", t0)])).toEqual(new Set());
  });
});
