import { useMemo } from "react";
import { PathLayer, ScatterplotLayer } from "@deck.gl/layers";
import { TripsLayer } from "@deck.gl/geo-layers";
import type {
  TrailDatum,
  TripDatum,
  PointDatum,
  Meta,
  Frame,
} from "@/types/trajectory";
import {
  C_TRAIL_ACTIVE,
  C_TRAIL_BEACHED,
  C_GLOW_ACTIVE,
  C_GLOW_BEACHED,
  C_BLOOM_ACTIVE,
  C_BLOOM_BEACHED,
  C_SELECTED,
  C_BEACHED,
  PALETTE_ORIGIN,
  activeColor,
} from "@/lib/constants";

type Layer =
  | PathLayer<TrailDatum>
  | TripsLayer<TripDatum>
  | ScatterplotLayer<PointDatum>;

type TripColor =
  | [number, number, number, number]
  | ((d: TripDatum) => [number, number, number, number]);

/** Thin streamlines, caps arrodonits, smooth joints; color fix o per origen */
function createTripsLayer(
  id: string,
  data: TripDatum[],
  color: TripColor,
  currentTimeSec: number,
  trailLengthSec: number,
  widthMinPixels: number,
  widthMaxPixels: number
): TripsLayer<TripDatum> {
  return new TripsLayer<TripDatum>({
    id,
    data,
    getPath: (d) => d.path.flatMap((p) => [p[0], p[1]]),
    getTimestamps: (d) => d.path.map((p) => p[2]),
    getColor: color,
    currentTime: currentTimeSec,
    trailLength: trailLengthSec,
    fadeTrail: true,
    widthMinPixels,
    widthMaxPixels,
    capRounded: true,
    jointRounded: true,
    positionFormat: "XY",
  });
}

function getColorByOrigin(
  meta: Meta | null,
  alpha: number
): (d: TripDatum) => [number, number, number, number] {
  return (d) => {
    const idx = meta?.origins?.[d.id] ?? 0;
    const [r, g, b] = PALETTE_ORIGIN[idx % PALETTE_ORIGIN.length];
    return [r, g, b, alpha];
  };
}

export function useMapLayers(
  currentFrame: Frame | null,
  scatterData: PointDatum[],
  trailData: TrailDatum[],
  tripDataWithTime: TripDatum[],
  currentTimeSec: number,
  trailLengthSec: number,
  meta: Meta | null,
  step: number,
  selectedParticleId: number | null,
  showTrails: boolean,
  showActiveParticles: boolean,
  showBeachedParticles: boolean,
  accumulatedBeachedPts: PointDatum[] = []
): Layer[] {
  const activePts = useMemo(() => {
    if (!scatterData.length) return [];
    return scatterData.filter((p) => {
      if (meta?.release_steps && meta.release_steps[p.id] > step) return false;
      return !p.beached;
    });
  }, [scatterData, meta, step]);

  const beachedPtsFromScatter = useMemo(() => {
    if (!scatterData.length) return [];
    return scatterData.filter((p) => {
      if (meta?.release_steps && meta.release_steps[p.id] > step)
        return false;
      return p.beached;
    });
  }, [scatterData, meta, step]);

  const beachedPts =
    accumulatedBeachedPts.length > 0 ? accumulatedBeachedPts : beachedPtsFromScatter;

  return useMemo(() => {
    if (!currentFrame) return [];
    const out: Layer[] = [];

    if (showTrails && tripDataWithTime.length > 0) {
      const activeTrips = tripDataWithTime.filter((t) => !t.beached);
      const beachedTrips = tripDataWithTime.filter((t) => t.beached);
      if (activeTrips.length) {
        const activeBloomColor = meta
          ? getColorByOrigin(meta, 40)
          : C_BLOOM_ACTIVE;
        const activeGlowColor = meta
          ? getColorByOrigin(meta, 120)
          : C_GLOW_ACTIVE;
        const activeCoreColor = meta
          ? getColorByOrigin(meta, 255)
          : C_TRAIL_ACTIVE;
        out.push(
          createTripsLayer(
            "trips-active-bloom",
            activeTrips,
            activeBloomColor,
            currentTimeSec,
            trailLengthSec,
            2,
            8
          )
        );
        out.push(
          createTripsLayer(
            "trips-active-glow",
            activeTrips,
            activeGlowColor,
            currentTimeSec,
            trailLengthSec,
            1,
            4
          )
        );
        out.push(
          createTripsLayer(
            "trips-active",
            activeTrips,
            activeCoreColor,
            currentTimeSec,
            trailLengthSec,
            0.8,
            2.2
          )
        );
      }
      if (beachedTrips.length) {
        out.push(
          createTripsLayer(
            "trips-beached-bloom",
            beachedTrips,
            C_BLOOM_BEACHED,
            currentTimeSec,
            trailLengthSec,
            1.5,
            5
          )
        );
        out.push(
          createTripsLayer(
            "trips-beached-glow",
            beachedTrips,
            C_GLOW_BEACHED,
            currentTimeSec,
            trailLengthSec,
            0.8,
            3
          )
        );
        out.push(
          createTripsLayer(
            "trips-beached",
            beachedTrips,
            C_TRAIL_BEACHED,
            currentTimeSec,
            trailLengthSec,
            0.6,
            2
          )
        );
      }
    }

    if (selectedParticleId !== null) {
      const selectedTrail = trailData.find((t) => t.id === selectedParticleId);
      if (selectedTrail) {
        out.push(
          new PathLayer<TrailDatum>({
            id: "trail-selected",
            data: [selectedTrail],
            getPath: (d) => d.path,
            getColor: C_SELECTED,
            getWidth: 3,
            widthUnits: "pixels",
            capRounded: true,
            jointRounded: true,
          })
        );
      }
    }

    if (showActiveParticles && activePts.length) {
      out.push(
        new ScatterplotLayer<PointDatum>({
          id: "p-a",
          data: activePts,
          getPosition: (d) => d.position,
          getFillColor: (d) =>
            d.id === selectedParticleId ? C_SELECTED : activeColor(d.speed),
          getRadius: (d) => (d.id === selectedParticleId ? 5 : 2.5),
          radiusUnits: "pixels",
          pickable: true,
        })
      );
    }
    if (showBeachedParticles && beachedPts.length) {
      out.push(
        new ScatterplotLayer<PointDatum>({
          id: "p-b",
          data: beachedPts,
          getPosition: (d) => d.position,
          getFillColor: (d) =>
            d.id === selectedParticleId ? C_SELECTED : C_BEACHED,
          getRadius: (d) => (d.id === selectedParticleId ? 5 : 3),
          radiusUnits: "pixels",
          pickable: true,
          stroked: true,
          lineWidthMinPixels: 1,
          getLineColor: [255, 255, 255, 80],
        })
      );
    }
    return out;
  }, [
    currentFrame,
    trailData,
    tripDataWithTime,
    activePts,
    beachedPts,
    currentTimeSec,
    trailLengthSec,
    selectedParticleId,
    showTrails,
    showActiveParticles,
    showBeachedParticles,
    meta,
  ]);
}
