import { useLayoutEffect, useMemo, useRef, useState } from "react";
import type { TimelineRange, TimelineRow } from "../../model/timelineTypes";
import {
  buildTimeTicks,
  buildTimelineLayout,
  TIMELINE_AXIS_HEIGHT,
  TIMELINE_BOTTOM_PADDING,
  TIMELINE_LABEL_WIDTH,
  TIMELINE_ZRV_BAND_HEIGHT,
  timeToX
} from "./tguTimelineLayout";
import { platformLabel } from "./tguTimelineMapper";
import { statusStyle } from "./tguStatus";
import { formatLocalDate, formatLocalDateTime, formatLocalTime, parseLocal } from "../../utils/localTime";

export type ZrvWindow = { startMs: number; endMs: number; kppId: string };

// Терминально-«мёртвые» статусы: план выбыл из живой цепочки (вытеснен закладкой, отклонён,
// не принят, сбой закладки, неоднозначный старт). Применяется ТОЛЬКО к «следующему» (обратная
// ссылка basePlanId → выбранный, она неоднозначна): мёртвый план — это, как правило, коррекция,
// проигравшая гонку закладки и оставшаяся со ссылкой base_plan_id на победителя (особенно старые
// строки до наследования предшественника), и без фильтра она ложно показывалась бы «следующей».
// На «предыдущий» НЕ распространяется: это явная база самого выбранного плана (forward-ссылка из
// его поля basePlanId) — её показываем всегда, даже если база REJECTED/SUPERSEDED.
const DEAD_LINK_STATUSES: ReadonlySet<string> = new Set([
  "SUPERSEDED",
  "REJECTED",
  "LAYIN_FAILED",
  "NOT_ACCEPTED",
  "START_AMBIGUOUS"
]);

type ZrvBands = {
  spacecraftId: string;
  windows: ZrvWindow[];
};

type TguTimelineProps = {
  rows: TimelineRow[];
  range: TimelineRange;
  selectedPlanId?: string;
  invalidRange?: boolean;
  /** ЗРВ выбранного КА — подсветка доступных зон на его строке. */
  zrvBands?: ZrvBands;
  /** id выбранной ЗРВ (`${kppId}|${startMs}`) — подсветка кликнутой зоны. */
  selectedZrvId?: string;
  /** КА с цепочкой на паузе после REJECTED — рисуем пометку у имени КА. */
  pausedSpacecraftIds?: ReadonlySet<string>;
  onSelectPlan: (planId: string) => void;
  /** Клик по ЗРВ: передаём пересекающиеся в этой точке зоны и индекс кликнутой. */
  onSelectZrv?: (windows: ZrvWindow[], selectedIndex: number) => void;
};

function zrvId(zrv: ZrvWindow): string {
  return `${zrv.kppId}|${zrv.startMs}`;
}

export function TguTimeline({
  rows,
  range,
  selectedPlanId,
  invalidRange,
  zrvBands,
  selectedZrvId,
  pausedSpacecraftIds,
  onSelectPlan,
  onSelectZrv
}: TguTimelineProps) {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const [containerWidth, setContainerWidth] = useState(1200);
  const timelineWidth = Math.max(720, containerWidth - TIMELINE_LABEL_WIDTH - 28);
  const fullWidth = TIMELINE_LABEL_WIDTH + timelineWidth;
  const nowMs = Date.now();

  useLayoutEffect(() => {
    if (!containerRef.current) return;

    setContainerWidth(Math.floor(containerRef.current.getBoundingClientRect().width));

    const observer = new ResizeObserver(([entry]) => {
      setContainerWidth(Math.floor(entry.contentRect.width));
    });
    observer.observe(containerRef.current);
    return () => observer.disconnect();
  }, []);

  const layout = useMemo(
    () => buildTimelineLayout(rows, range, timelineWidth),
    [rows, range, timelineWidth]
  );
  const segments = layout.segments;
  const height = Math.max(260, layout.height);
  // basePlanId выбранного плана — чтобы подсветить его прошлое/будущее звено по РЕАЛЬНОЙ связи
  // (а не по времени): предыдущий = план с этим planId, следующий = план с basePlanId == выбранный.
  const selectedBasePlanId = selectedPlanId
    ? segments.find((segment) => segment.plan.planId === selectedPlanId)?.plan.basePlanId ?? undefined
    : undefined;
  const ticks = useMemo(() => buildTimeTicks(range), [range]);
  const nowX =
    nowMs >= range.fromMs && nowMs <= range.toMs
      ? TIMELINE_LABEL_WIDTH + timeToX(nowMs, range, timelineWidth)
      : undefined;

  return (
    <div className="tgu-timeline" ref={containerRef}>
      {invalidRange ? (
        <div className="tgu-empty">Некорректный интервал: from должен быть меньше to.</div>
      ) : rows.length === 0 || segments.length === 0 ? (
        <div className="tgu-empty">Нет планов в выбранном интервале.</div>
      ) : (
      <svg className="tgu-timeline__svg" viewBox={`0 0 ${fullWidth} ${height}`} style={{ height }} role="img" aria-label="Timeline планов ТГУ">
        <rect x="0" y="0" width={fullWidth} height={height} className="tgu-timeline__background" />
        <rect x="0" y="0" width={TIMELINE_LABEL_WIDTH} height={height} className="tgu-timeline__labels-background" />

        <line
          x1={TIMELINE_LABEL_WIDTH}
          x2={fullWidth}
          y1={TIMELINE_AXIS_HEIGHT}
          y2={TIMELINE_AXIS_HEIGHT}
          className="tgu-timeline__axis"
        />

        {ticks.map((tick) => {
          const x = TIMELINE_LABEL_WIDTH + timeToX(tick, range, timelineWidth);
          return (
            <g key={tick}>
              <line x1={x} x2={x} y1="0" y2={height - TIMELINE_BOTTOM_PADDING} className="tgu-timeline__grid" />
              <text x={x + 6} y="18" className="tgu-timeline__tick-date">
                {formatTickDate(tick)}
              </text>
              <text x={x + 6} y="34" className="tgu-timeline__tick-time">
                {formatTickTime(tick)}
              </text>
            </g>
          );
        })}

        {nowX !== undefined && (
          <g>
            <line x1={nowX} x2={nowX} y1="0" y2={height - TIMELINE_BOTTOM_PADDING} className="tgu-timeline__now" />
            <text x={nowX + 7} y="16" className="tgu-timeline__now-label">
              сейчас
            </text>
          </g>
        )}

        {layout.rowLayouts.map(({ row, rowIndex, y, height: rowHeight, zrvBandY }) => {
          return (
            <g key={row.spacecraftId}>
              <rect
                x="0"
                y={y}
                width={fullWidth}
                height={rowHeight}
                className={rowIndex % 2 === 0 ? "tgu-timeline__row" : "tgu-timeline__row tgu-timeline__row--alt"}
              />
              <text x="16" y={y + 22} className="tgu-timeline__row-title">
                {platformLabel(row.platform, row.spacecraftId)}
              </text>
              <text x="16" y={y + 38} className="tgu-timeline__row-meta">
                NORAD {row.platform?.noradId || row.spacecraftId}
              </text>
              {pausedSpacecraftIds?.has(row.spacecraftId) && (
                <text
                  x={TIMELINE_LABEL_WIDTH - 12}
                  y={y + 22}
                  textAnchor="end"
                  className="tgu-timeline__paused-badge"
                >
                  ⏸ на паузе
                  <title>Цепочка на паузе: план отклонён — создайте исправленный план («Создать план»).</title>
                </text>
              )}
              {zrvBands?.spacecraftId === row.spacecraftId && (
                <>
                  <text x="16" y={zrvBandY + TIMELINE_ZRV_BAND_HEIGHT - 3} className="tgu-timeline__zrv-label">
                    ЗРВ
                  </text>
                  {zrvBands.windows.map((zrv, zrvIndex) => {
                    const from = Math.max(zrv.startMs, range.fromMs);
                    const to = Math.min(zrv.endMs, range.toMs);
                    if (to <= from) return null;
                    const bx = TIMELINE_LABEL_WIDTH + timeToX(from, range, timelineWidth);
                    const bw = Math.max(2, timeToX(to, range, timelineWidth) - timeToX(from, range, timelineWidth));
                    const id = zrvId(zrv);
                    return (
                      <rect
                        key={`zrv-${zrvIndex}`}
                        className={`tgu-timeline__zrv${selectedZrvId === id ? " is-selected" : ""}`}
                        x={bx}
                        y={zrvBandY}
                        width={bw}
                        height={TIMELINE_ZRV_BAND_HEIGHT}
                        style={onSelectZrv ? { cursor: "pointer" } : undefined}
                        onClick={(event) => {
                          if (!onSelectZrv) return;
                          event.stopPropagation();
                          // Зоны «в этой точке» — все ЗРВ, пересекающие кликнутую по времени
                          // (разные КПП могут перекрываться), как «заявки в точке» на карте.
                          const overlapping = zrvBands.windows.filter(
                            (w) => w.startMs < zrv.endMs && w.endMs > zrv.startMs
                          );
                          const idx = overlapping.findIndex((w) => zrvId(w) === id);
                          onSelectZrv(overlapping, Math.max(0, idx));
                        }}
                      >
                        <title>ЗРВ {zrv.kppId} · {formatLocalDateTime(zrv.startMs)} – {formatLocalDateTime(zrv.endMs)}</title>
                      </rect>
                    );
                  })}
                </>
              )}
            </g>
          );
        })}

        {segments.map((segment) => {
          const y = segment.centerY;
          const x1 = TIMELINE_LABEL_WIDTH + segment.x;
          const x2 = TIMELINE_LABEL_WIDTH + segment.x + segment.width;
          const style = statusStyle(segment.plan.status);
          const selected = selectedPlanId === segment.plan.planId;
          // Сосед выбранного по цепочке: предыдущий (прошлое) — planId == basePlanId выбранного;
          // следующий (будущее) — basePlanId == выбранный. Подсветка по реальной связи, не по времени.
          // prev — явная база выбранного плана (forward-ссылка), показываем всегда, даже мёртвую.
          // next — обратная ссылка; мёртвый план (вытесненный/отклонённый конкурент того же слота)
          // живым продолжением не подсвечиваем.
          const liveLink = !DEAD_LINK_STATUSES.has(segment.plan.status);
          const neighbor = selected
            ? undefined
            : selectedBasePlanId && segment.plan.planId === selectedBasePlanId
              ? "prev"
              : selectedPlanId && liveLink && segment.plan.basePlanId === selectedPlanId
                ? "next"
                : undefined;
          const labelVisible = segment.width > 92;
          // При активном выборе все «непричастные» планы гасим до еле заметных, а текущий /
          // прошлый / следующий обводим яркой рамкой — так видно звенья цепочки выбранного плана.
          const dimmed = selectedPlanId !== undefined && !selected && neighbor === undefined;
          // Снят с учёта: гасим план на таймлайне (но не сильнее, чем выбор-затемнение).
          const dismissed = Boolean(segment.plan.dismissedAt);

          return (
            <g
              key={segment.plan.planId}
              className={`tgu-timeline-plan ${selected ? "is-selected" : ""} ${dimmed ? "is-dimmed" : ""} ${
                dismissed ? "is-dismissed-plan" : ""
              }`}
              onClick={() => onSelectPlan(segment.plan.planId)}
            >
              <title>
                {segment.plan.planId} · {segment.plan.status}
                {dismissed ? " · снят с учёта" : ""}
                {neighbor === "prev" ? " · предыдущий в цепочке" : neighbor === "next" ? " · следующий в цепочке" : ""} ·{" "}
                {formatDateTime(segment.plan.startTime)} - {formatDateTime(segment.plan.endTime)}
              </title>
              {neighbor && (
                <line
                  x1={x1}
                  x2={x2}
                  y1={y}
                  y2={y}
                  className={`tgu-timeline-plan__neighbor tgu-timeline-plan__neighbor--${neighbor}`}
                />
              )}
              {selected && <line x1={x1} x2={x2} y1={y} y2={y} className="tgu-timeline-plan__selection" />}
              <line
                x1={x1}
                x2={x2}
                y1={y}
                y2={y}
                className={`tgu-timeline-plan__bar ${style.className}`}
                stroke={style.color}
              />
              {labelVisible && (
                <text x={x1 + 10} y={y + 4} className="tgu-timeline-plan__label">
                  {segment.plan.kppId} · {segment.plan.status}
                </text>
              )}
            </g>
          );
        })}
      </svg>
      )}
    </div>
  );
}

function formatTickDate(ms: number): string {
  return formatLocalDate(ms);
}

function formatTickTime(ms: number): string {
  return formatLocalTime(ms);
}

function formatDateTime(value: string): string {
  return formatLocalDateTime(parseLocal(value));
}
