// Общие утилиты, формат времени, раскладка интервалов по лейнам.
const D = window.MissionData;

const TYPE_COLOR = {
  optical: "var(--t-optical)",
  radar: "var(--t-radar)",
  combo: "var(--t-combo)",
};
const TYPE_DIM = {
  optical: "var(--t-optical-dim)",
  radar: "var(--t-radar-dim)",
  combo: "var(--t-combo-dim)",
};

// Стиль работы внутри плана по её категории. shoot тонируется типом аппаратуры,
// остальные — служебной палитрой, отличной от трёх цветов аппаратуры.
const WORK_KIND_COLOR = {
  shoot: null, // = цвет типа КА
  downlink: "var(--accent)",
  calib: "oklch(0.78 0.02 200)",
  service: "var(--ink-3)",
};
function workFill(kind, scType) {
  if (kind === "shoot") return TYPE_COLOR[scType];
  return WORK_KIND_COLOR[kind];
}

const MONTHS = ["янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек"];
const WDAYS = ["вс", "пн", "вт", "ср", "чт", "пт", "сб"];

function pad(n) { return String(n).padStart(2, "0"); }
function fmtTime(ms) {
  const d = new Date(ms);
  return `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`;
}
function fmtDate(ms) {
  const d = new Date(ms);
  return `${pad(d.getUTCDate())} ${MONTHS[d.getUTCMonth()]}`;
}
function fmtDateTime(ms) {
  const d = new Date(ms);
  return `${pad(d.getUTCDate())} ${MONTHS[d.getUTCMonth()]} ${fmtTime(ms)}`;
}
function fmtDur(ms) {
  const h = ms / 3600000;
  if (h < 24) return `${h % 1 === 0 ? h : h.toFixed(1)} ч`;
  const d = Math.floor(h / 24);
  const rh = Math.round(h - d * 24);
  return rh ? `${d} сут ${rh} ч` : `${d} сут`;
}

// Раскладка интервалов одного КА по лейнам (greedy interval graph).
function assignLanes(ivs) {
  const sorted = [...ivs].sort((a, b) => a.start - b.start || a.end - b.end);
  const laneEnds = []; // конец последнего интервала в каждом лейне
  const out = {};
  for (const iv of sorted) {
    let placed = false;
    for (let l = 0; l < laneEnds.length; l++) {
      if (iv.start >= laneEnds[l]) {
        out[iv.id] = l;
        laneEnds[l] = iv.end;
        placed = true;
        break;
      }
    }
    if (!placed) {
      out[iv.id] = laneEnds.length;
      laneEnds.push(iv.end);
    }
  }
  return { laneOf: out, laneCount: Math.max(1, laneEnds.length) };
}

// Пересечения по времени между интервалами одного КА.
function overlapSegments(ivs) {
  const segs = [];
  for (let i = 0; i < ivs.length; i++) {
    for (let j = i + 1; j < ivs.length; j++) {
      const a = ivs[i], b = ivs[j];
      const s = Math.max(a.start, b.start);
      const e = Math.min(a.end, b.end);
      if (e > s) segs.push({ start: s, end: e, a: a.id, b: b.id });
    }
  }
  return segs;
}

// Цепочка родословной (предки + потомки) для интервала.
function lineageOf(id, byId) {
  const anc = [];
  let cur = byId[id];
  while (cur && cur.parentId) {
    anc.unshift(cur.parentId);
    cur = byId[cur.parentId];
  }
  const desc = [];
  const stack = [...(D.childrenOf[id] || [])];
  while (stack.length) {
    const c = stack.shift();
    desc.push(c);
    (D.childrenOf[c] || []).forEach((x) => stack.push(x));
  }
  return { ancestors: anc, descendants: desc, chain: [...anc, id, ...desc] };
}

// "Хорошие" шаги сетки времени для оси.
function niceTicks(rangeStart, rangeEnd, pxPerMs, minPx) {
  const HOUR = 3600000, DAY = 86400000;
  const steps = [1 * HOUR, 2 * HOUR, 3 * HOUR, 6 * HOUR, 12 * HOUR, 1 * DAY, 2 * DAY, 7 * DAY];
  let step = steps[steps.length - 1];
  for (const s of steps) { if (s * pxPerMs >= minPx) { step = s; break; } }
  const ticks = [];
  const startTick = Math.ceil(rangeStart / step) * step;
  for (let t = startTick; t <= rangeEnd; t += step) ticks.push({ t, step });
  return { ticks, step };
}

Object.assign(window, {
  TYPE_COLOR, TYPE_DIM, MONTHS, WDAYS, WORK_KIND_COLOR, workFill,
  pad, fmtTime, fmtDate, fmtDateTime, fmtDur,
  assignLanes, overlapSegments, lineageOf, niceTicks,
});
