import { describe, expect, it } from "vitest";
import type { TguPlanUi } from "../../model/timelineTypes";
import {
  addDownlink,
  addShoot,
  deleteWork,
  mutate,
  planModesToWorks,
  redo,
  seedDraft,
  undo,
  updateWork
} from "./editorDraft";
import type { PlanMode } from "./editorApi";
import type { DraftHistory } from "./model/editorTypes";
import { parseLocal } from "../../utils/localTime";

const plan: TguPlanUi = {
  planId: "plan-1",
  spacecraftId: "56756",
  startTime: "2026-05-29T01:00:00",
  endTime: "2026-05-29T02:00:00",
  kppId: "KPP-1",
  status: "PLANNED",
  startMs: parseLocal("2026-05-29T01:00:00"),
  endMs: parseLocal("2026-05-29T02:00:00")
};

describe("editorDraft", () => {
  it("seeds an empty draft when the plan has no embedded works", () => {
    // Существующие включения грузятся отдельно (planModesToWorks); черновик хранит только правки.
    expect(seedDraft(plan)).toEqual({
      planId: "plan-1",
      spacecraftId: "56756",
      works: []
    });
  });

  it("maps plan modes to read-only timeline works", () => {
    const modes: PlanMode[] = [
      {
        id: 11,
        planId: 1,
        timeStart: "2026-05-29T01:10:00",
        revolution: 4321,
        type: "SURVEY",
        status: "PLANNED",
        lat: 55.7,
        longitude: 37.6,
        duration: 180,
        contourWkt: null,
        roll: 0
      },
      {
        id: 22,
        planId: 1,
        timeStart: "2026-05-29T01:40:00",
        revolution: 4322,
        type: "DROP",
        station: "102",
        duration: 120,
        surveys: [11]
      },
      {
        id: 33,
        planId: 1,
        timeStart: null,
        revolution: 4323,
        type: "SURVEY",
        status: "PLANNED",
        lat: 0,
        longitude: 0,
        duration: 60,
        contourWkt: null,
        roll: 0
      }
    ];

    const works = planModesToWorks(modes);

    // Включение без времени старта пропускается, оба валидных — read-only (origin: "plan").
    expect(works).toEqual([
      {
        id: "survey-11",
        kind: "shoot",
        label: "Съёмка · виток 4321",
        startMs: parseLocal("2026-05-29T01:10:00"),
        endMs: parseLocal("2026-05-29T01:10:00") + 180_000,
        origin: "plan",
        planModeId: 11,
        revolution: 4321,
        rollDeg: 0,
        centerLat: 55.7,
        centerLon: 37.6,
        targetLat: 55.7,
        targetLon: 37.6
      },
      {
        id: "drop-22",
        kind: "downlink",
        label: "Сброс · 102",
        startMs: parseLocal("2026-05-29T01:40:00"),
        endMs: parseLocal("2026-05-29T01:40:00") + 120_000,
        origin: "plan",
        planModeId: 22,
        revolution: 4322,
        stationId: "102",
        surveyIds: [11]
      }
    ]);
  });

  it("adds shoot and downlink works with deterministic ids", () => {
    const draft = seedDraft(undefined, "56756");
    const withShoot = addShoot(draft, {
      id: "shoot-1",
      label: "Панхром. съёмка",
      modeId: "pan",
      startMs: 1000,
      endMs: 2000,
      target: { id: "target-1", name: "Цель A", lat: 55.7, lon: 37.6 }
    });
    const withDownlink = addDownlink(withShoot, {
      id: "downlink-1",
      label: "Сброс на НКПОР",
      startMs: 3000,
      endMs: 4000,
      station: { id: "KPP-1", name: "KPP-1" }
    });

    expect(withDownlink.works.map((work) => work.id)).toEqual(["shoot-1", "downlink-1"]);
    expect(withDownlink.works.every((work) => work.origin === "added" && work.isNew)).toBe(true);
  });

  it("keeps undo and redo stacks consistent across mutations", () => {
    const initial: DraftHistory = { draft: seedDraft(undefined, "56756"), past: [], future: [] };
    const changed = mutate(initial, (draft) =>
      addDownlink(draft, {
        id: "downlink-1",
        label: "Сброс",
        startMs: 1000,
        endMs: 2000,
        station: { id: "KPP-1", name: "KPP-1" }
      })
    );

    const undone = undo(changed);
    const redone = redo(undone);

    expect(changed.past).toHaveLength(1);
    expect(undone.draft.works).toHaveLength(0);
    expect(undone.future).toHaveLength(1);
    expect(redone.draft.works).toHaveLength(1);
    expect(redone.future).toHaveLength(0);
  });

  it("updates and deletes works without generating ids internally", () => {
    const draft = addDownlink(seedDraft(plan), {
      id: "downlink-1",
      label: "Сброс на НКПОР",
      startMs: plan.startMs,
      endMs: plan.startMs + 120_000,
      station: { id: "KPP-1", name: "KPP-1" }
    });
    const updated = updateWork(draft, "downlink-1", { label: "Сеанс", startMs: plan.startMs + 60_000 });
    const deleted = deleteWork(updated, "downlink-1");

    expect(updated.works[0].label).toBe("Сеанс");
    expect(deleted.works.map((work) => work.id)).toEqual([]);
  });
});
