// Таймлайн (Gantt): ось времени, сетка, маркер «сейчас», секции групп,
// раскладка интервалов по лейнам, пометка пересечений, стрелки родословной.
const { useRef: useRefTL, useMemo: useMemoTL, useEffect: useEffectTL, useLayoutEffect } = React;

const LABEL_W = 234;
const LANE_H = 38;
const ROW_PAD = 8;
const GROUP_H = 30;
const AXIS_H = 46;

function Timeline(props) {
  const { rows, pxPerMs, rangeStart, rangeEnd, now, selectedIv, onSelectIv,
    lineageSet, byId, scrollSignal, timelineCollapsed, toggleCollapse } = props;

  const bodyRef = useRefTL(null);
  const axisRef = useRefTL(null);
  const leftRef = useRefTL(null);

  const x = (ms) => (ms - rangeStart) * pxPerMs;
  const contentW = (rangeEnd - rangeStart) * pxPerMs;

  // Раскладка строк по вертикали + позиции интервалов.
  const layout = useMemoTL(() => {
    let y = 0;
    const placed = [];
    const ivPos = {};
    for (const r of rows) {
      if (r.kind === "group") {
        placed.push({ ...r, y, h: GROUP_H });
        y += GROUP_H;
      } else {
        const { laneOf, laneCount } = assignLanes(r.ivs);
        const h = laneCount * LANE_H + ROW_PAD * 2;
        const overlaps = overlapSegments(r.ivs);
        for (const iv of r.ivs) {
          const lane = laneOf[iv.id];
          ivPos[iv.id] = {
            x: x(iv.start), x2: x(iv.end),
            cy: y + ROW_PAD + lane * LANE_H + LANE_H / 2,
          };
        }
        placed.push({ ...r, y, h, laneOf, laneCount, overlaps });
        y += h;
      }
    }
    return { placed, total: y, ivPos };
  }, [rows, pxPerMs, rangeStart]);

  // Сетка по дням + засечки оси.
  const days = useMemoTL(() => {
    const out = [];
    const startD = new Date(rangeStart);
    let t = Date.UTC(startD.getUTCFullYear(), startD.getUTCMonth(), startD.getUTCDate());
    while (t <= rangeEnd) {
      const d = new Date(t);
      out.push({ t, weekend: d.getUTCDay() === 0 || d.getUTCDay() === 6, dow: d.getUTCDay() });
      t += 86400000;
    }
    return out;
  }, [rangeStart, rangeEnd]);

  const sub = useMemoTL(() => niceTicks(rangeStart, rangeEnd, pxPerMs, 64), [rangeStart, rangeEnd, pxPerMs]);
  const showHourTicks = sub.step < 86400000;

  // Синхронизация скролла.
  const onBodyScroll = () => {
    const b = bodyRef.current;
    if (axisRef.current) axisRef.current.scrollLeft = b.scrollLeft;
    if (leftRef.current) leftRef.current.scrollTop = b.scrollTop;
  };

  // Прокрутка к «сейчас».
  useEffectTL(() => {
    const b = bodyRef.current;
    if (!b) return;
    const target = x(now) - b.clientWidth * 0.32;
    b.scrollTo({ left: Math.max(0, target), behavior: scrollSignal === 0 ? "auto" : "smooth" });
    onBodyScroll();
  }, [scrollSignal]);

  // Стрелки родословной для выделенной цепочки.
  const arrows = useMemoTL(() => {
    if (!selectedIv) return [];
    const lin = lineageOf(selectedIv, byId);
    const segs = [];
    for (const id of lin.chain) {
      (D.childrenOf[id] || []).forEach((c) => {
        if (lin.chain.includes(c) && layout.ivPos[id] && layout.ivPos[c]) {
          segs.push({ from: layout.ivPos[id], to: layout.ivPos[c] });
        }
      });
    }
    return segs;
  }, [selectedIv, layout, lineageSet]);

  const anySelected = !!selectedIv;

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%", background: "var(--bg-0)", minWidth: 0 }}>
      {/* ОСЬ ВРЕМЕНИ */}
      <div style={{ display: "flex", height: AXIS_H, flex: "none", borderBottom: "1px solid var(--line)", background: "var(--bg-1)" }}>
        <div style={{ width: LABEL_W, flex: "none", borderRight: "1px solid var(--line)", display: "flex", alignItems: "flex-end", padding: "0 12px 7px" }}>
          <span className="mono" style={{ fontSize: 9.5, color: "var(--ink-3)", letterSpacing: ".1em" }}>КОСМИЧЕСКИЙ АППАРАТ</span>
        </div>
        <div ref={axisRef} style={{ flex: 1, overflow: "hidden", position: "relative" }}>
          <div style={{ width: contentW, height: "100%", position: "relative" }}>
            {/* верхний ярус — дни */}
            {days.map((d, i) => {
              const w = 86400000 * pxPerMs;
              return (
                <div key={i} style={{ position: "absolute", left: x(d.t), top: 0, width: w, height: 24, borderLeft: "1px solid var(--line-soft)", display: "flex", alignItems: "center", gap: 5, padding: "0 7px", color: d.weekend ? "var(--ink-3)" : "var(--ink-1)" }}>
                  <span style={{ fontSize: 11, fontWeight: 600 }}>{pad(new Date(d.t).getUTCDate())}</span>
                  <span style={{ fontSize: 9.5, color: "var(--ink-3)" }}>{MONTHS[new Date(d.t).getUTCMonth()]}</span>
                  <span className="mono" style={{ fontSize: 8.5, color: "var(--ink-3)", letterSpacing: ".05em" }}>{WDAYS[d.dow]}</span>
                </div>
              );
            })}
            {/* нижний ярус — засечки */}
            {sub.ticks.map((tk, i) => (
              <div key={i} style={{ position: "absolute", left: x(tk.t), top: 24, height: 22, borderLeft: "1px solid var(--grid)", paddingLeft: 4, display: "flex", alignItems: "center" }}>
                {showHourTicks && <span className="mono" style={{ fontSize: 9, color: "var(--ink-3)" }}>{fmtTime(tk.t)}</span>}
              </div>
            ))}
            {/* метка «сейчас» */}
            <div style={{ position: "absolute", left: x(now), top: 0, height: "100%", transform: "translateX(-50%)", display: "flex", alignItems: "flex-start" }}>
              <span className="mono" style={{ marginTop: 3, fontSize: 9, fontWeight: 600, color: "var(--bg-0)", background: "var(--now)", padding: "1px 5px", borderRadius: 4, whiteSpace: "nowrap", boxShadow: "0 0 8px var(--now)" }}>СЕЙЧАС {fmtTime(now)}</span>
            </div>
          </div>
        </div>
      </div>

      {/* ТЕЛО */}
      <div style={{ flex: 1, display: "flex", minHeight: 0 }}>
        {/* Левая колонка подписей */}
        <div ref={leftRef} style={{ width: LABEL_W, flex: "none", overflow: "hidden", borderRight: "1px solid var(--line)", background: "var(--bg-1)" }}>
          <div style={{ height: layout.total, position: "relative" }}>
            {layout.placed.map((r) => r.kind === "group" ? (
              <div key={"g" + r.group.id} onClick={() => toggleCollapse(r.group.id)}
                style={{ position: "absolute", top: r.y, left: 0, right: 0, height: r.h, display: "flex", alignItems: "center", gap: 7, padding: "0 12px", background: "var(--bg-2)", borderBottom: "1px solid var(--line)", borderTop: "1px solid var(--line)", cursor: "pointer" }}>
                <svg width="9" height="9" viewBox="0 0 10 10" style={{ transform: timelineCollapsed.has(r.group.id) ? "none" : "rotate(90deg)", transition: "transform .12s", color: "var(--ink-2)", flex: "none" }}><path d="M3 1.5 7 5 3 8.5" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round" /></svg>
                <span className="mono" style={{ fontSize: 9.5, color: "var(--ink-3)" }}>{r.group.code}</span>
                <span style={{ flex: 1, fontSize: 12, fontWeight: 700 }}>{r.group.name}</span>
                <span className="mono tnum" style={{ fontSize: 10, color: "var(--ink-3)" }}>{r.count}</span>
              </div>
            ) : (
              <div key={r.sc.id} style={{ position: "absolute", top: r.y, left: 0, right: 0, height: r.h, display: "flex", alignItems: "center", gap: 8, padding: "0 12px", borderBottom: "1px solid var(--line-soft)" }}>
                <TypeDot type={r.sc.type} size={8} />
                <div style={{ minWidth: 0, flex: 1 }}>
                  <div className="mono" style={{ fontSize: 11.5, color: "var(--ink-1)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{r.sc.short}</div>
                  <div style={{ fontSize: 9.5, color: "var(--ink-3)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{r.sc.orbit}</div>
                </div>
                <span className="mono tnum" style={{ fontSize: 9.5, color: "var(--ink-3)" }}>{r.ivs.length}</span>
              </div>
            ))}
          </div>
        </div>

        {/* Правый холст */}
        <div ref={bodyRef} onScroll={onBodyScroll} style={{ flex: 1, overflow: "auto", position: "relative" }}>
          <div style={{ width: contentW, height: layout.total, position: "relative" }}>
            {/* фон выходных + сетка дней */}
            {days.map((d, i) => (
              <div key={i} style={{ position: "absolute", left: x(d.t), top: 0, width: 86400000 * pxPerMs, height: "100%", borderLeft: "1px solid var(--grid)", background: d.weekend ? "oklch(0.22 0.012 165 / 0.5)" : "transparent" }} />
            ))}
            {/* линия «сейчас» */}
            <div style={{ position: "absolute", left: x(now), top: 0, height: "100%", width: 0, borderLeft: "1.5px dashed var(--now)", zIndex: 6 }} />

            {/* строки */}
            {layout.placed.map((r) => r.kind === "group" ? (
              <div key={"g" + r.group.id} style={{ position: "absolute", top: r.y, left: 0, width: contentW, height: r.h, background: "var(--bg-2)", borderTop: "1px solid var(--line)", borderBottom: "1px solid var(--line)", opacity: .95 }} />
            ) : (
              <Row key={r.sc.id} r={r} x={x} contentW={contentW} selectedIv={selectedIv}
                onSelectIv={onSelectIv} lineageSet={lineageSet} anySelected={anySelected} />
            ))}

            {/* стрелки родословной */}
            {arrows.length > 0 && (
              <svg style={{ position: "absolute", left: 0, top: 0, width: contentW, height: layout.total, pointerEvents: "none", zIndex: 7 }}>
                <defs>
                  <marker id="ah" markerWidth="7" markerHeight="7" refX="5.5" refY="3" orient="auto">
                    <path d="M0 0 L6 3 L0 6 z" fill="var(--accent)" />
                  </marker>
                </defs>
                {arrows.map((a, i) => {
                  const x1 = a.from.x2, y1 = a.from.cy, x2 = a.to.x, y2 = a.to.cy;
                  const mid = x1 + Math.max(14, (x2 - x1) / 2);
                  const d = `M ${x1} ${y1} C ${mid} ${y1}, ${mid} ${y2}, ${x2 - 3} ${y2}`;
                  return <path key={i} d={d} fill="none" stroke="var(--accent)" strokeWidth="1.6" markerEnd="url(#ah)" opacity="0.9" />;
                })}
              </svg>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

// Одна строка КА: ПЛАНЫ (объемлющие интервалы) по лейнам, внутри — РАБОТЫ.
function Row({ r, x, contentW, selectedIv, onSelectIv, lineageSet, anySelected }) {
  return (
    <div style={{ position: "absolute", top: r.y, left: 0, width: contentW, height: r.h, borderBottom: "1px solid var(--line-soft)" }}>
      {/* пересечения планов — штриховка */}
      {r.overlaps.map((ov, i) => (
        <div key={"ov" + i} className="hatch" style={{ position: "absolute", left: x(ov.start), width: Math.max(2, x(ov.end) - x(ov.start)), top: 2, height: r.h - 4, borderLeft: "1px solid var(--now)", borderRight: "1px solid var(--now)", borderRadius: 2, zIndex: 1, pointerEvents: "none" }} />
      ))}
      {r.ivs.map((plan) => {
        const lane = r.laneOf[plan.id];
        const left = x(plan.start);
        const w = Math.max(4, x(plan.end) - x(plan.start));
        const inChain = lineageSet && lineageSet.has(plan.id);
        const isSel = selectedIv === plan.id;
        const dim = anySelected && !inChain;
        const scType = r.sc.type;
        const col = TYPE_COLOR[scType];
        const hasParent = !!plan.parentId;
        const hasChild = (D.childrenOf[plan.id] || []).length > 0;
        const bandTop = ROW_PAD + lane * LANE_H;
        const bandH = LANE_H - 8;
        const wide = w > 78;
        const showWorks = w > 14;
        const executed = plan.status === "executed";
        return (
          <div key={plan.id} onClick={(e) => { e.stopPropagation(); onSelectIv(plan.id); }}
            title={`План ${plan.id} · ${plan.title} · ${fmtDateTime(plan.start)} → ${fmtDateTime(plan.end)} · работ: ${plan.works.length}`}
            style={{
              position: "absolute", left, width: w, top: bandTop, height: bandH,
              background: `color-mix(in oklch, ${col} 13%, var(--bg-1))`,
              border: `1px solid color-mix(in oklch, ${col} 45%, var(--bg-1))`,
              borderLeft: `3px solid ${col}`,
              borderRadius: 5, cursor: "pointer", zIndex: isSel ? 5 : 3,
              opacity: dim ? 0.26 : executed ? 0.7 : 1,
              outline: isSel ? `1.5px solid var(--accent)` : inChain ? `1px solid color-mix(in oklch, var(--accent) 55%, transparent)` : "none",
              boxShadow: isSel ? "0 0 0 3px oklch(0.82 0.13 200 / 0.18), var(--shadow)" : "none",
              overflow: "hidden", transition: "opacity .12s",
            }}>
            {/* заголовок плана */}
            <div style={{ display: "flex", alignItems: "center", gap: 4, height: 14, padding: "0 4px 0 5px" }}>
              {hasParent && <span style={{ flex: "none", color: "var(--accent)", fontSize: 8, lineHeight: 1 }} title="есть предок">◀</span>}
              {wide && <span className="mono" style={{ flex: 1, fontSize: 9, color: "var(--ink-1)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", letterSpacing: ".01em" }}>{plan.title}</span>}
              {wide && plan.status === "active" && <span style={{ width: 4, height: 4, borderRadius: 9, background: "var(--now)", boxShadow: "0 0 5px var(--now)", flex: "none" }} />}
              {wide && plan.rev > 1 && <span className="mono" style={{ flex: "none", fontSize: 7.5, color: "var(--ink-3)", border: "1px solid var(--line)", borderRadius: 3, padding: "0 2px" }}>r{plan.rev}</span>}
              {!wide && <span style={{ flex: 1 }} />}
              {hasChild && <span style={{ flex: "none", color: "var(--accent)", fontSize: 8, lineHeight: 1 }} title="есть потомок">▶</span>}
            </div>
            {/* трек работ внутри плана */}
            {showWorks && (
              <div style={{ position: "absolute", left: 0, right: 0, bottom: 2, height: 11 }}>
                {plan.works.map((wk) => {
                  const wl = x(wk.start) - left;
                  const ww = Math.max(1.5, x(wk.end) - x(wk.start));
                  const fill = workFill(wk.kind, scType);
                  const isService = wk.kind === "service";
                  return (
                    <div key={wk.id} title={`${wk.label} · ${fmtTime(wk.start)}–${fmtTime(wk.end)}`}
                      style={{
                        position: "absolute", left: wl, width: ww, top: 0, height: 11,
                        background: isService ? "transparent" : fill,
                        border: isService ? `1px dashed ${fill}` : "none",
                        borderRadius: 2, boxSizing: "border-box",
                      }} />
                  );
                })}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

Object.assign(window, { Timeline });
