/**
 * 어구 그룹 궤적 리플레이 스토어
 *
 * loadHistory() 시 전처리를 수행하고 결과를 상태로 보관.
 * rAF 루프로 currentTime을 갱신 → useGearReplayLayers 훅이 구독하여 렌더링.
 */
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';
import {
  buildMemberTripsData,
  buildCenterTrailData,
  buildSnapshotRanges,
  buildMemberMetadata,
  type HistoryFrame,
  type TripsLayerDatum,
  type CenterTrailSegment,
  type MemberMeta,
} from './gearReplayPreprocess';

/** 24시간을 30초에 재생: SPEED_FACTOR = (24 * 3600 * 1000) / (30 * 1000) = 2880 */
const SPEED_FACTOR = 2880;

interface CorrelationItem {
  targetMmsi: string;
  targetName: string;
  score: number;
  freezeState: string;
}

interface GearReplayState {
  // timeline
  isPlaying: boolean;
  currentTime: number;
  startTime: number;
  endTime: number;
  playbackSpeed: number;

  // data (원본)
  groupKey: string | null;
  historyFrames: HistoryFrame[];
  correlationItems: CorrelationItem[];

  // 전처리 결과
  frameTimes: number[];
  memberTripsData: TripsLayerDatum[];
  memberMetadata: Map<string, MemberMeta>;
  centerTrailSegments: CenterTrailSegment[];
  centerDotsPositions: [number, number][];
  snapshotRanges: number[];
  dataStartTime: number;
  dataEndTime: number;

  // 후보 선박 항적
  candidateTripsData: TripsLayerDatum[];
  candidateMetadata: Map<string, { name: string }>;

  // actions
  loadHistory: (
    groupKey: string,
    frames: HistoryFrame[],
    correlations: CorrelationItem[],
    candidateTracks?: { vesselId: string; shipName: string; geometry: [number, number][]; timestamps: string[] }[],
  ) => void;
  play: () => void;
  pause: () => void;
  seek: (time: number) => void;
  setSpeed: (speed: number) => void;
  reset: () => void;
}

// Module-level rAF state — React re-render 유발 없음
let animFrameId = 0;
let lastFrameTime = 0;

function clamp(value: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, value));
}

export const useGearReplayStore = create<GearReplayState>()(
  subscribeWithSelector((set, get) => ({
    // timeline
    isPlaying: false,
    currentTime: 0,
    startTime: 0,
    endTime: 0,
    playbackSpeed: 1,

    // data
    groupKey: null,
    historyFrames: [],
    correlationItems: [],

    // 전처리 결과
    frameTimes: [],
    memberTripsData: [],
    memberMetadata: new Map(),
    centerTrailSegments: [],
    centerDotsPositions: [],
    snapshotRanges: [],
    dataStartTime: 0,
    dataEndTime: 0,

    // 후보 선박 항적
    candidateTripsData: [],
    candidateMetadata: new Map(),

    loadHistory: (groupKey, frames, correlations, candidateTracks) => {
      get().pause();

      const sorted = [...frames].sort(
        (a, b) => new Date(a.snapshotTime).getTime() - new Date(b.snapshotTime).getTime(),
      );

      const frameTimes = sorted.map(f => new Date(f.snapshotTime).getTime());
      const dataStartTime = frameTimes.length > 0 ? frameTimes[0] : Date.now() - 86_400_000;
      const dataEndTime = frameTimes.length > 0 ? frameTimes[frameTimes.length - 1] : Date.now();

      // 타임라인은 데이터 실제 범위 사용
      const startTime = dataStartTime;
      const endTime = dataEndTime;

      // 전처리
      const memberTripsData = buildMemberTripsData(sorted, startTime);
      const memberMetadata = buildMemberMetadata(sorted);
      const { segments, dots } = buildCenterTrailData(sorted);
      const snapshotRanges = buildSnapshotRanges(sorted, startTime, endTime);

      // 후보 선박 항적 전처리
      const candidateTripsData: TripsLayerDatum[] = [];
      const candidateMetadata = new Map<string, { name: string }>();
      if (candidateTracks) {
        for (const track of candidateTracks) {
          if (track.geometry.length < 2) continue;
          candidateMetadata.set(track.vesselId, { name: track.shipName });
          // timestamps: Unix초 문자열 → startTime 기준 상대 ms
          const relTs = track.timestamps.map(t => Number(t) * 1000 - startTime);
          candidateTripsData.push({
            id: track.vesselId,
            path: track.geometry,
            timestamps: relTs,
            color: [16, 185, 129, 200], // emerald (후보 선박)
          });
        }
      }

      set({
        groupKey,
        historyFrames: sorted,
        correlationItems: correlations,
        frameTimes,
        startTime,
        endTime,
        dataStartTime,
        dataEndTime,
        memberTripsData, memberMetadata,
        centerTrailSegments: segments,
        centerDotsPositions: dots,
        snapshotRanges,
        candidateTripsData, candidateMetadata,
        currentTime: dataStartTime,
        isPlaying: false,
      });
    },

    play: () => {
      const state = get();
      if (state.isPlaying) return;

      const startFrom =
        state.currentTime >= state.endTime ? state.startTime : state.currentTime;

      set({ isPlaying: true, currentTime: startFrom });
      lastFrameTime = performance.now();

      const tick = (now: number) => {
        const { isPlaying, currentTime, endTime, playbackSpeed } = get();
        if (!isPlaying) return;

        const delta = now - lastFrameTime;
        lastFrameTime = now;

        const newTime = currentTime + delta * SPEED_FACTOR * playbackSpeed;

        if (newTime >= endTime) {
          set({ currentTime: endTime, isPlaying: false });
          animFrameId = 0;
          return;
        }

        set({ currentTime: newTime });
        animFrameId = requestAnimationFrame(tick);
      };

      animFrameId = requestAnimationFrame(tick);
    },

    pause: () => {
      if (animFrameId !== 0) {
        cancelAnimationFrame(animFrameId);
        animFrameId = 0;
      }
      set({ isPlaying: false });
    },

    seek: (time) => {
      const { startTime, endTime } = get();
      set({ currentTime: clamp(time, startTime, endTime) });
    },

    setSpeed: (speed) => {
      set({ playbackSpeed: speed });
    },

    reset: () => {
      if (animFrameId !== 0) {
        cancelAnimationFrame(animFrameId);
        animFrameId = 0;
      }
      set({
        isPlaying: false,
        currentTime: 0,
        startTime: 0,
        endTime: 0,
        playbackSpeed: 1,
        groupKey: null,
        historyFrames: [],
        correlationItems: [],
        frameTimes: [],
        memberTripsData: [],
        memberMetadata: new Map(),
        centerTrailSegments: [],
        centerDotsPositions: [],
        snapshotRanges: [],
        candidateTripsData: [],
        candidateMetadata: new Map(),
        dataStartTime: 0,
        dataEndTime: 0,
      });
    },
  })),
);
