import type { TguPlanUi } from "../../model/timelineTypes";
import type {
  Draft,
  DraftHistory,
  EditorDownlinkWork,
  EditorGroundStation,
  EditorShootWork,
  EditorTarget,
  EditorWork,
  EditorWorkPatch
} from "./model/editorTypes";

const DEFAULT_DUPLICATE_OFFSET_MS = 20 * 60 * 1000;
const DEFAULT_HISTORY_LIMIT = 50;

export type AddShootInput = {
  id: string;
  startMs: number;
  endMs: number;
  label: string;
  modeId?: string;
  target: EditorTarget;
};

export type AddDownlinkInput = {
  id: string;
  startMs: number;
  endMs: number;
  label: string;
  station: EditorGroundStation;
};

export function seedDraft(plan?: TguPlanUi, spacecraftId?: string): Draft {
  if (!plan) {
    return { spacecraftId, works: [] };
  }

  const embeddedWorks = readEmbeddedWorks(plan);
  return {
    planId: plan.planId,
    spacecraftId: plan.spacecraftId,
    works: embeddedWorks.length > 0 ? embeddedWorks : [fallbackPlanWork(plan)]
  };
}

export function mutate(
  history: DraftHistory,
  transform: (draft: Draft) => Draft,
  historyLimit = DEFAULT_HISTORY_LIMIT
): DraftHistory {
  const nextDraft = transform(history.draft);
  if (nextDraft === history.draft) {
    return history;
  }

  return {
    draft: nextDraft,
    past: [...history.past, history.draft].slice(-historyLimit),
    future: []
  };
}

export function undo(history: DraftHistory): DraftHistory {
  if (history.past.length === 0) {
    return history;
  }

  return {
    draft: history.past[history.past.length - 1],
    past: history.past.slice(0, -1),
    future: [history.draft, ...history.future]
  };
}

export function redo(history: DraftHistory): DraftHistory {
  if (history.future.length === 0) {
    return history;
  }

  return {
    draft: history.future[0],
    past: [...history.past, history.draft],
    future: history.future.slice(1)
  };
}

export function addShoot(draft: Draft, input: AddShootInput): Draft {
  const work: EditorShootWork = {
    id: input.id,
    kind: "shoot",
    label: input.label,
    startMs: input.startMs,
    endMs: input.endMs,
    modeId: input.modeId,
    targetName: input.target.name,
    targetLat: input.target.lat,
    targetLon: input.target.lon,
    origin: "added",
    isNew: true
  };

  return { ...draft, works: [...draft.works, work] };
}

export function addDownlink(draft: Draft, input: AddDownlinkInput): Draft {
  const work: EditorDownlinkWork = {
    id: input.id,
    kind: "downlink",
    label: input.label,
    startMs: input.startMs,
    endMs: input.endMs,
    stationId: input.station.id,
    origin: "added",
    isNew: true
  };

  return { ...draft, works: [...draft.works, work] };
}

export function updateWork(draft: Draft, workId: string, patch: EditorWorkPatch): Draft {
  return {
    ...draft,
    works: draft.works.map((work) => (work.id === workId ? ({ ...work, ...patch } as EditorWork) : work))
  };
}

export function deleteWork(draft: Draft, workId: string): Draft {
  return {
    ...draft,
    works: draft.works.filter((work) => work.id !== workId)
  };
}

export function duplicateWork(
  draft: Draft,
  workId: string,
  newId: string,
  offsetMs = DEFAULT_DUPLICATE_OFFSET_MS
): Draft {
  const source = draft.works.find((work) => work.id === workId);
  if (!source) {
    return draft;
  }

  const duplicated: EditorWork = {
    ...source,
    id: newId,
    startMs: source.startMs + offsetMs,
    endMs: source.endMs + offsetMs,
    origin: "added",
    isNew: true
  };

  return { ...draft, works: [...draft.works, duplicated] };
}

function fallbackPlanWork(plan: TguPlanUi): EditorDownlinkWork {
  return {
    id: `${plan.planId}:plan-window`,
    kind: "downlink",
    label: `План ТГУ · ${plan.kppId}`,
    startMs: plan.startMs,
    endMs: plan.endMs,
    stationId: plan.kppId,
    origin: "plan"
  };
}

function readEmbeddedWorks(plan: TguPlanUi): EditorWork[] {
  const rawWorks = (plan as TguPlanUi & { works?: unknown }).works;
  if (!Array.isArray(rawWorks)) {
    return [];
  }

  return rawWorks.flatMap((value, index): EditorWork[] => {
    if (!isRecord(value)) return [];
    const kind = value.kind === "shoot" || value.kind === "downlink" ? value.kind : undefined;
    const startMs = readTime(value, "startMs") ?? readTime(value, "start");
    const endMs = readTime(value, "endMs") ?? readTime(value, "end");
    if (!kind || startMs === undefined || endMs === undefined || startMs >= endMs) {
      return [];
    }

    const id = readString(value.id) ?? `${plan.planId}:work-${index + 1}`;
    const label = readString(value.label) ?? (kind === "shoot" ? "Съёмка" : "Сброс на НКПОР");
    const base = {
      id,
      kind,
      label,
      startMs,
      endMs,
      origin: "plan" as const
    };

    if (kind === "shoot") {
      return [{
        ...base,
        kind,
        modeId: readString(value.modeId),
        targetName: readString(value.targetName),
        targetLat: readNumber(value.targetLat),
        targetLon: readNumber(value.targetLon)
      }];
    }

    return [{
      ...base,
      kind,
      stationId: readString(value.stationId) ?? readString(value.kppId)
    }];
  });
}

function readTime(record: Record<string, unknown>, key: string): number | undefined {
  const value = record[key];
  if (typeof value === "number" && Number.isFinite(value)) {
    return value;
  }
  if (typeof value === "string") {
    const parsed = new Date(value).getTime();
    return Number.isFinite(parsed) ? parsed : undefined;
  }
  return undefined;
}

function readString(value: unknown): string | undefined {
  return typeof value === "string" && value.trim() ? value : undefined;
}

function readNumber(value: unknown): number | undefined {
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}
