import { ScatterplotLayer, PathLayer, PolygonLayer } from '@deck.gl/layers';
import type {
  ReplayShip,
  CollisionEvent,
  ReplayPathPoint,
  BackwardParticleStep,
} from '@common/types/backtrack';
import { hexToRgba } from './mapUtils';

// Andrew's monotone chain — 전체 파티클 경로의 외각 폴리곤 계산
function convexHull(points: [number, number][]): [number, number][] {
  if (points.length < 3) return points;
  const cross = (O: [number, number], A: [number, number], B: [number, number]) =>
    (A[0] - O[0]) * (B[1] - O[1]) - (A[1] - O[1]) * (B[0] - O[0]);
  const sorted = [...points].sort((a, b) => (a[0] !== b[0] ? a[0] - b[0] : a[1] - b[1]));
  const lower: [number, number][] = [];
  for (const p of sorted) {
    while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0)
      lower.pop();
    lower.push(p);
  }
  const upper: [number, number][] = [];
  for (let i = sorted.length - 1; i >= 0; i--) {
    const p = sorted[i];
    while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0)
      upper.pop();
    upper.push(p);
  }
  lower.pop();
  upper.pop();
  return [...lower, ...upper];
}

function getInterpolatedPosition(
  path: ReplayPathPoint[],
  frame: number,
  totalFrames: number,
): { lat: number; lon: number; segmentIndex: number } {
  const progress = Math.min(frame / totalFrames, 1);
  const floatIndex = progress * (path.length - 1);
  const idx = Math.min(Math.floor(floatIndex), path.length - 2);
  const frac = floatIndex - idx;
  return {
    lat: path[idx].lat + (path[idx + 1].lat - path[idx].lat) * frac,
    lon: path[idx].lon + (path[idx + 1].lon - path[idx].lon) * frac,
    segmentIndex: idx,
  };
}

interface BacktrackReplayParams {
  replayShips: ReplayShip[];
  collisionEvent: CollisionEvent | null;
  replayFrame: number;
  totalFrames: number;
  incidentCoord: { lat: number; lon: number };
  backwardParticles?: BackwardParticleStep[];
}

const BACKWARD_COLOR: [number, number, number, number] = [168, 85, 247, 160];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createBacktrackLayers(params: BacktrackReplayParams): any[] {
  const {
    replayShips,
    collisionEvent,
    replayFrame,
    totalFrames,
    incidentCoord,
    backwardParticles,
  } = params;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const layers: any[] = [];
  const progress = replayFrame / totalFrames;

  // Per-ship track lines + waypoints + ship position
  const allTrackData: Array<{ path: [number, number][]; color: [number, number, number, number] }> =
    [];
  const allWaypoints: Array<{
    position: [number, number];
    color: [number, number, number, number];
  }> = [];
  const allShipPositions: Array<{
    position: [number, number];
    color: [number, number, number, number];
    name: string;
  }> = [];

  replayShips.forEach((ship) => {
    const pos = getInterpolatedPosition(ship.path, replayFrame, totalFrames);
    const trackPath: [number, number][] = ship.path
      .slice(0, pos.segmentIndex + 2)
      .map((p, i, arr) => {
        if (i === arr.length - 1) return [pos.lon, pos.lat];
        return [p.lon, p.lat];
      });

    const rgba = hexToRgba(ship.color, 180);
    allTrackData.push({ path: trackPath, color: rgba });

    ship.path.slice(0, pos.segmentIndex + 1).forEach((p) => {
      allWaypoints.push({ position: [p.lon, p.lat], color: hexToRgba(ship.color, 130) });
    });

    allShipPositions.push({
      position: [pos.lon, pos.lat],
      color: hexToRgba(ship.color),
      name: ship.vesselName,
    });
  });

  // Track lines
  layers.push(
    new PathLayer({
      id: 'bt-tracks',
      data: allTrackData,
      getPath: (d: (typeof allTrackData)[0]) => d.path,
      getColor: (d: (typeof allTrackData)[0]) => d.color,
      getWidth: 2,
      getDashArray: [6, 4],
      dashJustified: true,
      widthMinPixels: 2,
      extensions: [],
    }),
  );

  // Waypoint dots
  layers.push(
    new ScatterplotLayer({
      id: 'bt-waypoints',
      data: allWaypoints,
      getPosition: (d: (typeof allWaypoints)[0]) => d.position,
      getRadius: 3,
      getFillColor: (d: (typeof allWaypoints)[0]) => d.color,
      radiusMinPixels: 3,
      radiusMaxPixels: 5,
    }),
  );

  // Ship position markers
  layers.push(
    new ScatterplotLayer({
      id: 'bt-ships',
      data: allShipPositions,
      getPosition: (d: (typeof allShipPositions)[0]) => d.position,
      getRadius: 8,
      getFillColor: (d: (typeof allShipPositions)[0]) => d.color,
      getLineColor: [255, 255, 255, 200],
      getLineWidth: 2,
      stroked: true,
      radiusMinPixels: 8,
      radiusMaxPixels: 14,
      pickable: true,
    }),
  );

  // Collision point
  const collisionProgress = collisionEvent ? collisionEvent.progressPercent / 100 : 0.75;
  const showCollision = progress >= collisionProgress;

  if (showCollision && collisionEvent) {
    layers.push(
      new ScatterplotLayer({
        id: 'bt-collision',
        data: [{ position: [collisionEvent.position.lon, collisionEvent.position.lat] }],
        getPosition: (d: { position: [number, number] }) => d.position,
        getRadius: 12,
        getFillColor: [239, 68, 68, 80],
        getLineColor: [239, 68, 68, 200],
        getLineWidth: 2,
        stroked: true,
        radiusMinPixels: 12,
        pickable: true,
      }),
    );

    // Oil spill expansion
    const spillSize = Math.min(
      500,
      ((progress - collisionProgress) / (1 - collisionProgress)) * 500,
    );
    if (spillSize > 0) {
      layers.push(
        new ScatterplotLayer({
          id: 'bt-spill',
          data: [{ position: [incidentCoord.lon, incidentCoord.lat], radius: spillSize }],
          getPosition: (d: { position: [number, number] }) => d.position,
          getRadius: (d: { radius: number }) => d.radius,
          getFillColor: [249, 115, 22, 50],
          getLineColor: [249, 115, 22, 100],
          getLineWidth: 1,
          stroked: true,
          radiusUnits: 'meters' as const,
        }),
      );
    }
  }

  // 역방향 예측 파티클 + 전체 경로 외각 폴리곤
  if (backwardParticles && backwardParticles.length > 0) {
    // 전체 스텝의 모든 파티클을 합쳐 외각 폴리곤 계산 (정적 — 항상 표시)
    const allPoints = backwardParticles.flat();
    if (allPoints.length >= 3) {
      const hull = convexHull(allPoints);
      if (hull.length >= 3) {
        layers.push(
          new PolygonLayer({
            id: 'bt-backward-hull',
            data: [{ polygon: hull }],
            getPolygon: (d: { polygon: [number, number][] }) => d.polygon,
            getFillColor: [168, 85, 247, 18],
            getLineColor: [168, 85, 247, 110],
            getLineWidth: 1,
            stroked: true,
            filled: true,
            lineWidthMinPixels: 1,
          }),
        );
      }
    }

    // 현재 프레임 파티클
    const stepIndex = Math.round((1 - progress) * (backwardParticles.length - 1));
    const particles = backwardParticles[stepIndex] ?? [];
    if (particles.length > 0) {
      layers.push(
        new ScatterplotLayer({
          id: 'bt-backward-particles',
          data: particles,
          getPosition: (d: [number, number]) => d,
          getRadius: 3,
          getFillColor: BACKWARD_COLOR,
          radiusMinPixels: 2.5,
          radiusMaxPixels: 5,
        }),
      );
    }
  }

  return layers;
}
