import { useEffect, useMemo, useRef, useState } from "react";
import { formatDate, formatDateTime, formatTick, formatTime, kindColor, kindLabel, niceTicks } from "./editorPresentation";
import type {
  EditorContextLane,
  EditorTimelineWindow,
  EditorVisibilityTrack,
  EditorWork
} from "./model/editorTypes";
import { VisibilityTracks } from "./VisibilityTracks";

type EditorTimelineProps = {
  activeLabel: string;
  works: EditorWork[];
  selectedWorkId?: string;
  window: EditorTimelineWindow;
  planStartMs?: number;
  planEndMs?: number;
  conflicts: Record<string, string[]>;
  contextLanes: EditorContextLane[];
  visibilityTracks: EditorVisibilityTrack[];
  onSelectWork: (workId: string) => void;
  onClearSelection: () => void;
  /** Масштаб колесом мыши: factor (>1 — отдалить, <1 — приблизить) и время под курсором. */
  onWheelZoom?: (factor: number, anchorMs: number) => void;
  /** Сдвиг окна перетаскиванием: новое значение левой границы окна (мс). */
  onPanTo?: (fromMs: number) => void;
};

const LABEL_WIDTH = 168;
const AXIS_HEIGHT = 40;
/** Минимальная высота активной дорожки (два ряда: съёмка + сброс). */
const MIN_ACTIVE_HEIGHT = 84;
const VISIBILITY_HEIGHT = 24;
const OTHER_HEIGHT = 30;

export function EditorTimeline({
  activeLabel,
  works,
  selectedWorkId,
  window: timelineWindow,
  planStartMs,
  planEndMs,
  conflicts,
  contextLanes,
  visibilityTracks,
  onSelectWork,
  onClearSelection,
  onWheelZoom,
  onPanTo
}: EditorTimelineProps) {
  const wrapperRef = useRef<HTMLDivElement | null>(null);
  const [bodyWidth, setBodyWidth] = useState(900);
  const [bodyHeight, setBodyHeight] = useState(260);
  const [grabbing, setGrabbing] = useState(false);
  const span = Math.max(1, timelineWindow.toMs - timelineWindow.fromMs);
  const pxPerMs = bodyWidth / span;
  const toX = (timeMs: number) => (timeMs - timelineWindow.fromMs) * pxPerMs;
  const { ticks, step: tickStep } = useMemo(
    () => niceTicks(timelineWindow.fromMs, timelineWindow.toMs, pxPerMs, 72),
    [pxPerMs, timelineWindow.fromMs, timelineWindow.toMs]
  );
  const days = useMemo(() => buildDays(timelineWindow), [timelineWindow]);
  // Фиксированная высота частей ниже активной дорожки (треки видимости + контекст КА).
  const belowActiveHeight =
    visibilityTracks.length * VISIBILITY_HEIGHT +
    (contextLanes.length > 0 ? 22 : 0) +
    contextLanes.length * OTHER_HEIGHT +
    8;
  // Активная дорожка резиновая: занимает всё свободное место панели, поэтому при
  // подъёме панели вверх ряды съёмки/сброса расширяются, а низ не пустует.
  const activeHeight = Math.max(MIN_ACTIVE_HEIGHT, bodyHeight - AXIS_HEIGHT - belowActiveHeight);
  const visibilityTop = AXIS_HEIGHT + activeHeight;
  const dividerTop = visibilityTop + visibilityTracks.length * VISIBILITY_HEIGHT;
  const contextTop = dividerTop + (contextLanes.length > 0 ? 22 : 0);
  const contentHeight = AXIS_HEIGHT + activeHeight + belowActiveHeight;
  const surfaceHeight = Math.max(bodyHeight, contentHeight);

  useEffect(() => {
    const element = wrapperRef.current;
    if (!element) return;

    const measure = () => {
      setBodyWidth(Math.max(560, element.clientWidth - LABEL_WIDTH));
      setBodyHeight(element.clientHeight);
    };

    const observer = new ResizeObserver(measure);
    observer.observe(element);
    measure();

    return () => observer.disconnect();
  }, []);

  // Свежие значения окна/геометрии для нативного wheel-слушателя (он навешивается один раз).
  const wheelStateRef = useRef({ fromMs: timelineWindow.fromMs, pxPerMs, bodyWidth, onWheelZoom });
  wheelStateRef.current = { fromMs: timelineWindow.fromMs, pxPerMs, bodyWidth, onWheelZoom };

  useEffect(() => {
    const element = wrapperRef.current;
    if (!element) return;

    // Нативный слушатель с passive: false — иначе preventDefault не сработает и страница
    // прокрутится вместо масштабирования таймлайна.
    const handleWheel = (event: WheelEvent) => {
      const state = wheelStateRef.current;
      if (!state.onWheelZoom) return;
      event.preventDefault();
      const rect = element.getBoundingClientRect();
      const x = event.clientX - rect.left - LABEL_WIDTH;
      const clampedX = Math.min(Math.max(x, 0), state.bodyWidth);
      const anchorMs = state.fromMs + clampedX / state.pxPerMs;
      const factor = event.deltaY > 0 ? 1.1 : 0.9;
      state.onWheelZoom(factor, anchorMs);
    };

    element.addEventListener("wheel", handleWheel, { passive: false });
    return () => element.removeEventListener("wheel", handleWheel);
  }, []);

  // Перетаскивание окна по горизонтали (как карту). На mousedown фиксируем точку и левую
  // границу окна, дальше двигаем абсолютно (без накопления дрейфа). Сдвиг > порога помечаем
  // как drag, чтобы погасить последующий click (иначе выделение/сброс включения).
  const dragRef = useRef<{ startX: number; startFromMs: number; pxPerMs: number; moved: boolean } | null>(null);
  const suppressClickRef = useRef(false);

  const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
    if (event.button !== 0 || !onPanTo || pxPerMs <= 0) return;
    const drag = { startX: event.clientX, startFromMs: timelineWindow.fromMs, pxPerMs, moved: false };
    dragRef.current = drag;

    const onMove = (move: MouseEvent) => {
      const deltaPx = move.clientX - drag.startX;
      if (Math.abs(deltaPx) > 3) {
        drag.moved = true;
        setGrabbing(true);
      }
      // Тянем содержимое вправо → показываем более раннее время (окно едет влево).
      onPanTo(drag.startFromMs - deltaPx / drag.pxPerMs);
    };
    const onUp = () => {
      if (drag.moved) suppressClickRef.current = true;
      dragRef.current = null;
      setGrabbing(false);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
  };

  // Капче-фаза: если только что был drag — гасим click до того, как он дойдёт до полос/фона.
  const handleClickCapture = (event: React.MouseEvent<HTMLDivElement>) => {
    if (suppressClickRef.current) {
      suppressClickRef.current = false;
      event.stopPropagation();
      event.preventDefault();
    }
  };

  return (
    <div
      className="tgu-editor-timeline"
      ref={wrapperRef}
      onClick={onClearSelection}
      onClickCapture={handleClickCapture}
      onMouseDown={handleMouseDown}
      style={onPanTo ? { cursor: grabbing ? "grabbing" : "grab" } : undefined}
    >
      <div className="tgu-editor-timeline__surface" style={{ width: LABEL_WIDTH + bodyWidth, height: Math.max(220, surfaceHeight) }}>
        <div className="tgu-editor-timeline__axis-label" style={{ width: LABEL_WIDTH }}>
          Время
        </div>
        <div className="tgu-editor-timeline__axis" style={{ left: LABEL_WIDTH, width: bodyWidth }}>
          {days.map((day) => (
            <div className="tgu-editor-timeline__day" key={day} style={{ left: toX(day) }}>
              <strong>{formatDate(day).split(" ")[0]}</strong>
              <span>{formatDate(day).split(" ")[1]}</span>
            </div>
          ))}
          {ticks.map((tick) => (
            <div className="tgu-editor-timeline__tick-label" key={tick} style={{ left: toX(tick) }}>
              {formatTick(tick, tickStep)}
            </div>
          ))}
        </div>

        <div className="tgu-editor-timeline__grid" style={{ left: LABEL_WIDTH, top: AXIS_HEIGHT, width: bodyWidth }}>
          {ticks.map((tick) => (
            <div key={tick} style={{ left: toX(tick) }} />
          ))}
        </div>

        {planStartMs != null && planStartMs >= timelineWindow.fromMs && planStartMs <= timelineWindow.toMs && (
          <div className="tgu-editor-timeline__bound tgu-editor-timeline__bound--start" style={{ left: LABEL_WIDTH + toX(planStartMs), top: AXIS_HEIGHT }}>
            <span>Начало плана</span>
          </div>
        )}

        {planEndMs != null && planEndMs >= timelineWindow.fromMs && planEndMs <= timelineWindow.toMs && (
          <div className="tgu-editor-timeline__bound tgu-editor-timeline__bound--end" style={{ left: LABEL_WIDTH + toX(planEndMs), top: AXIS_HEIGHT }}>
            <span>Конец плана</span>
          </div>
        )}

        <div className="tgu-editor-timeline__lane" style={{ top: AXIS_HEIGHT, height: activeHeight }}>
          <div className="tgu-editor-timeline__lane-label tgu-editor-timeline__lane-label--split" style={{ width: LABEL_WIDTH }}>
            <span>{activeLabel}</span>
            <small>редактируется</small>
            <em className="tgu-editor-timeline__row-hint tgu-editor-timeline__row-hint--shoot">Съёмка</em>
            <em className="tgu-editor-timeline__row-hint tgu-editor-timeline__row-hint--downlink">Сброс</em>
          </div>
          <div className="tgu-editor-timeline__lane-body" style={{ left: LABEL_WIDTH, width: bodyWidth }}>
            {works.map((work) => {
              const left = toX(work.startMs);
              const width = Math.max(6, toX(work.endMs) - toX(work.startMs));
              const color = kindColor(work.kind);
              const selected = selectedWorkId === work.id;
              const hasConflict = Boolean(conflicts[work.id]?.length);

              return (
                <button
                  className={`tgu-editor-work tgu-editor-work--${work.kind} ${selected ? "is-selected" : ""} ${hasConflict ? "has-conflict" : ""}`}
                  type="button"
                  key={work.id}
                  style={{
                    left,
                    width,
                    borderLeftColor: color,
                    background: `color-mix(in srgb, ${color} 22%, var(--bg-1))`
                  }}
                  title={`${kindLabel(work.kind)} · ${formatDateTime(work.startMs)}-${formatTime(work.endMs)}`}
                  onClick={(event) => {
                    event.stopPropagation();
                    onSelectWork(work.id);
                  }}
                >
                  <span className="tgu-editor-work__dot" style={{ background: color }} />
                  {width > 54 && <span className="tgu-editor-work__label">{work.label}</span>}
                  {work.isNew && <span className="tgu-editor-work__new">NEW</span>}
                  {width > 82 && <small>{formatTime(work.startMs)}-{formatTime(work.endMs)}</small>}
                </button>
              );
            })}
          </div>
        </div>

        <VisibilityTracks
          tracks={visibilityTracks}
          window={timelineWindow}
          width={bodyWidth}
          labelWidth={LABEL_WIDTH}
          top={visibilityTop}
          rowHeight={VISIBILITY_HEIGHT}
        />

        {contextLanes.length > 0 && (
          <div className="tgu-editor-context-divider" style={{ top: dividerTop, width: LABEL_WIDTH + bodyWidth }}>
            Другие КА · контекст (read-only)
          </div>
        )}

        {contextLanes.map((lane, index) => (
          <div className="tgu-editor-timeline__lane" key={lane.spacecraftId} style={{ top: contextTop + index * OTHER_HEIGHT, height: OTHER_HEIGHT }}>
            <div className="tgu-editor-timeline__lane-label" style={{ width: LABEL_WIDTH }}>
              <span>{lane.label}</span>
            </div>
            <div className="tgu-editor-timeline__lane-body" style={{ left: LABEL_WIDTH, width: bodyWidth }}>
              {lane.works.map((work) => {
                if (work.endMs < timelineWindow.fromMs || work.startMs > timelineWindow.toMs) return null;
                const left = toX(work.startMs);
                const width = Math.max(3, toX(work.endMs) - toX(work.startMs));
                return (
                  <div
                    className="tgu-editor-work tgu-editor-work--context"
                    key={work.id}
                    style={{ left, width, borderLeftColor: kindColor(work.kind) }}
                    title={`${lane.label} · ${kindLabel(work.kind)} · ${formatTime(work.startMs)}-${formatTime(work.endMs)}`}
                  />
                );
              })}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function buildDays(window: EditorTimelineWindow): number[] {
  const result: number[] = [];
  const start = new Date(window.fromMs);
  let dayMs = new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime();
  while (dayMs <= window.toMs) {
    result.push(dayMs);
    dayMs += 24 * 60 * 60 * 1000;
  }
  return result;
}
