import { useEffect, useRef, useState } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import type { Map, GeoJSONSource } from 'maplibre-gl'
import { MapLibreViewer } from '../maplibre/MapLibreViewer'
import { useMapLibreMap } from '../maplibre/maplibreContext'
import { safeRemoveLayers } from '../maplibre/geoUtils'
import { createCustom3DLayer } from '../maplibre/custom3DLayer'
import type { Layer3DHandle } from '../maplibre/custom3DLayer'
import { fetchChallengeDetail } from '../api/challenges'
import { simulateCoaster, saveCoaster, listCoasters } from '../api/coaster'
import { useCoasterPath } from './useCoasterPath'
import { useAccelerationStrips, computeArcLengths, snapToPath } from './useAccelerationStrips'
import { useTerrainCapture } from './useTerrainCapture'
import { RideRenderer } from './RideRenderer'
import { SimulationPlots } from './SimulationPlots'
import { AccelerationStripsPanel } from './AccelerationStripsPanel'
import { CoasterListPanel } from './CoasterListPanel'
import { effectiveLngLatAlt } from './bezierUtils'
import { useAuthStore } from '../store/authStore'
import type { ChallengeDetail, CoasterSimulationResult, SavedCoaster } from '../types/api'
import type { AnchorPoint } from './bezierUtils'
import styles from './CoasterEditorPage.module.css'

// ── Route pages ───────────────────────────────────────────────────────────────

export function CoasterEditorPage() {
  const { id } = useParams<{ id: string }>()
  const [challenge, setChallenge] = useState<ChallengeDetail | null>(null)

  useEffect(() => {
    if (!id) return
    fetchChallengeDetail(id).then(setChallenge).catch(console.error)
  }, [id])

  return (
    <MapLibreViewer>
      <CoasterEditorScene challengeId={id} challenge={challenge} />
    </MapLibreViewer>
  )
}

export function CoasterViewerPage() {
  const { challengeId, coasterId } = useParams<{ challengeId: string; coasterId: string }>()
  const [challenge, setChallenge] = useState<ChallengeDetail | null>(null)
  const [preloadCoaster, setPreloadCoaster] = useState<SavedCoaster | null>(null)

  useEffect(() => {
    if (!challengeId) return
    fetchChallengeDetail(challengeId).then(setChallenge).catch(console.error)
    listCoasters(challengeId)
      .then((list) => {
        const found = list.find((c) => c.id === coasterId)
        if (found) setPreloadCoaster(found)
      })
      .catch(console.error)
  }, [challengeId, coasterId])

  return (
    <MapLibreViewer>
      <CoasterEditorScene
        challengeId={challengeId}
        challenge={challenge}
        readonly
        preloadCoaster={preloadCoaster ?? undefined}
      />
    </MapLibreViewer>
  )
}

// ── Source / layer IDs for editor-specific overlays ───────────────────────────

const SRC_REGION    = 'editor-region'
const LYR_REGION_F  = 'editor-region-fill'
const LYR_REGION_L  = 'editor-region-line'
const LYR_SIM_RAILS = 'sim-rails-3d'

function emptyFC(): GeoJSON.FeatureCollection { return { type: 'FeatureCollection', features: [] } }
function setData(map: Map, src: string, data: GeoJSON.GeoJSON) {
  (map.getSource(src) as GeoJSONSource | undefined)?.setData(data)
}

// ── Inner scene (needs map context) ──────────────────────────────────────────

interface SceneProps {
  challengeId: string | undefined
  challenge: ChallengeDetail | null
  readonly?: boolean
  preloadCoaster?: SavedCoaster
}

function CoasterEditorScene({ challengeId, challenge, readonly = false, preloadCoaster }: SceneProps) {
  const map      = useMapLibreMap()
  const navigate = useNavigate()

  const simRailLayerRef = useRef<Layer3DHandle | null>(null)

  const authUser = useAuthStore(s => s.user)
  const currentUsername = authUser?.profile?.preferred_username as string | undefined

  const [coasterName, setCoasterName] = useState('')
  const [simResult, setSimResult]     = useState<CoasterSimulationResult | null>(null)
  const [simulating, setSimulating]   = useState(false)
  const [simError, setSimError]       = useState<string | null>(null)
  const [initialVelocity, setInitialVelocity] = useState(1.0)
  const [showPath, setShowPath]       = useState(true)
  const [showAnchors, setShowAnchors] = useState(true)
  const [showStrips, setShowStrips]   = useState(true)
  const [coasterListKey, setCoasterListKey] = useState(0)
  const [isRideMode, setIsRideMode]   = useState(false)
  const [rideCursor, setRideCursor]   = useState<number | null>(null)
  const [pathHover, setPathHover]     = useState<{ x: number; y: number; pct: number } | null>(null)

  const path    = useCoasterPath(map, showPath, showAnchors)
  const accel   = useAccelerationStrips(map, path.pathPts, path.mode === 'strip', showStrips)
  const terrain = useTerrainCapture(map, simResult)

  // Auto-load preloaded coaster (viewer mode)
  useEffect(() => {
    if (preloadCoaster) handleLoad(preloadCoaster)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [preloadCoaster])

  // Exit ride mode when sim result changes
  useEffect(() => { setIsRideMode(false); setRideCursor(null) }, [simResult])

  // ── Set up region + sim-rail sources ──────────────────────────────────────

  useEffect(() => {
    map.addSource(SRC_REGION, { type: 'geojson', data: emptyFC() })
    map.addLayer({ id: LYR_REGION_F, type: 'fill', source: SRC_REGION,
      paint: { 'fill-color': '#06b6d4', 'fill-opacity': 0.04 } })
    map.addLayer({ id: LYR_REGION_L, type: 'line', source: SRC_REGION,
      paint: { 'line-color': '#06b6d4', 'line-opacity': 0.45, 'line-width': 2 } })

    const handle = createCustom3DLayer(LYR_SIM_RAILS, map)
    simRailLayerRef.current = handle

    return () => {
      handle.destroy()
      simRailLayerRef.current = null
      safeRemoveLayers(map, [LYR_REGION_F, LYR_REGION_L], [SRC_REGION])
    }
  }, [map])

  // ── Fly to challenge region ────────────────────────────────────────────────

  useEffect(() => {
    if (!challenge) return

    const coords = challenge.region.coordinates[0] as [number, number][]
    const lons = coords.map(c => c[0])
    const lats = coords.map(c => c[1])
    map.fitBounds(
      [[Math.min(...lons), Math.min(...lats)], [Math.max(...lons), Math.max(...lats)]],
      { padding: 60, pitch: 55, bearing: 0, duration: 1200 },
    )

    setData(map, SRC_REGION, { type: 'Feature', geometry: challenge.region, properties: {} })
  }, [challenge, map])

  // ── Simulation result rails (custom 3D layer at absolute altitude) ────────

  useEffect(() => {
    if (!simResult) {
      simRailLayerRef.current?.update([])
      return
    }

    simRailLayerRef.current?.update([
      {
        pts:         simResult.rail_1 as [number, number, number][],
        color:       0xef4444,
        radiusMeters: 0.12,
      },
      {
        pts:         simResult.rail_2 as [number, number, number][],
        color:       0xef4444,
        radiusMeters: 0.12,
      },
    ])
  }, [simResult])

  // ── Path hover tooltip ────────────────────────────────────────────────────

  useEffect(() => {
    if (path.pathPts.length < 2) { setPathHover(null); return }

    const arcs = computeArcLengths(path.pathPts)

    function onMove(e: { point: { x: number; y: number }; lngLat: { lng: number; lat: number } }) {
      const { x, y } = e.point
      const hits = map.queryRenderedFeatures(
        [{ x: x - 4, y: y - 4 }, { x: x + 4, y: y + 4 }],
        { layers: ['coaster-path-lyr'] },
      )
      if (hits.length === 0) { setPathHover(null); return }
      const { frac } = snapToPath([e.lngLat.lng, e.lngLat.lat], path.pathPts, arcs)
      setPathHover({ x, y, pct: frac * 100 })
    }

    map.on('mousemove', onMove)
    return () => { map.off('mousemove', onMove) }
  }, [map, path.pathPts])

  // ── Load / Save handlers ─────────────────────────────────────────────────

  function handleLoad(coaster: SavedCoaster) {
    const anchorPoints: AnchorPoint[] = coaster.anchors.map(a => ({
      id: a.id,
      lngLat: [a.lon, a.lat] as [number, number],
      terrainHeight: a.terrainAlt,
      heightOffset: a.heightOffset,
    }))
    path.loadAnchors(anchorPoints)
    accel.loadStrips(coaster.acceleration_strips)
    setCoasterName(coaster.name || '')
    setSimResult(null)
  }

  async function handleSave() {
    if (!challengeId || path.anchors.length < 2) return
    const storedAnchors = path.anchors.map(a => ({
      id: a.id,
      lon: a.lngLat[0],
      lat: a.lngLat[1],
      terrainAlt: a.terrainHeight,
      heightOffset: a.heightOffset,
    }))
    await saveCoaster(challengeId, {
      name: coasterName.trim(),
      anchors: storedAnchors,
      acceleration_strips: accel.strips,
    })
    setCoasterListKey(k => k + 1)
  }

  // ── Simulate handler ──────────────────────────────────────────────────────

  async function handleSimulate() {
    if (path.anchors.length < 2) return
    setSimulating(true)
    setSimError(null)

    try {
      const geoPath = path.anchors.map(anchor => effectiveLngLatAlt(anchor))

      const result = await simulateCoaster({
        path: geoPath,
        params: { initial_velocity: initialVelocity },
        acceleration_strips: accel.strips.map(s => ({
          start_frac: s.startFrac,
          end_frac:   s.endFrac,
          accel_ms2:  s.accel_ms2,
        })),
      })
      setSimResult(result)
    } catch (err: unknown) {
      const msg = err instanceof Error ? err.message : 'Simulation failed'
      setSimError(msg)
    } finally {
      setSimulating(false)
    }
  }

  // ── Derived UI state ──────────────────────────────────────────────────────

  const selected = path.anchors.find(a => a.id === path.selectedId)
  const selectedIndex = path.anchors.findIndex(a => a.id === path.selectedId)
  const diag = simResult?.diagnostics

  return (
    <>
      {/* ── Three.js ride renderer (fullscreen, mounts over map) ──────────── */}
      {isRideMode && simResult && terrain.captureData && (
        <RideRenderer
          simResult={simResult}
          captureData={terrain.captureData}
          onStop={() => { setIsRideMode(false); setRideCursor(null) }}
        />
      )}

      {/* ── Top bar ─────────────────────────────────────────────────────── */}
      <div className={styles.topBar}>
        <button className={styles.backBtn} onClick={() => navigate(-1)}>
          ← Back
        </button>
        {!readonly && (
          <input
            className={styles.nameInput}
            type="text"
            placeholder="Name your coaster…"
            value={coasterName}
            onChange={(e) => setCoasterName(e.target.value)}
            maxLength={255}
          />
        )}
        <h1 className={styles.title}>
          {challenge ? challenge.title : 'Loading…'}
        </h1>
        <span className={styles.badge}>{readonly ? 'Viewer' : 'Coaster Editor'}</span>
        {challengeId && (
          <CoasterListPanel
            challengeId={challengeId}
            currentUsername={currentUsername}
            onLoad={handleLoad}
            refreshKey={coasterListKey}
            menuMode
          />
        )}
      </div>

      {/* ── Mode toolbar (hidden while riding) ──────────────────────────── */}
      {!isRideMode && (
        <div className={styles.toolbar}>
          {!readonly && (
            <>
              <ModeButton label="Add Point"     active={path.mode === 'add'}    onClick={() => path.setMode('add')} />
              <ModeButton label="Select / Move" active={path.mode === 'select'} onClick={() => path.setMode('select')} />
              <ModeButton label="Strip"         active={path.mode === 'strip'}  onClick={() => path.setMode('strip')} />
              <div className={styles.divider} />
              <button className={styles.ghostBtn} onClick={path.undoLast} disabled={path.anchors.length === 0}>
                Undo
              </button>
              <button className={styles.clearBtn} onClick={path.clearAll} disabled={path.anchors.length === 0}>
                Clear
              </button>
              {path.anchors.length > 0 && (
                <span className={styles.countBadge}>
                  {path.anchors.length} pt{path.anchors.length !== 1 ? 's' : ''}
                </span>
              )}
              <div className={styles.divider} />
            </>
          )}

          <button
            className={`${styles.simulateBtn}${simulating ? ` ${styles.simulating}` : ''}`}
            onClick={handleSimulate}
            disabled={path.anchors.length < 2 || simulating}
          >
            {simulating ? 'Simulating…' : 'Simulate'}
          </button>

          <div className={styles.divider} />
          <button
            className={`${styles.toolBtn}${showPath ? ` ${styles.active}` : ''}`}
            onClick={() => setShowPath(p => !p)}
          >
            Path
          </button>
          <button
            className={`${styles.toolBtn}${showAnchors ? ` ${styles.active}` : ''}`}
            onClick={() => setShowAnchors(a => !a)}
          >
            Anchors
          </button>
          <button
            className={`${styles.toolBtn}${showStrips ? ` ${styles.active}` : ''}`}
            onClick={() => setShowStrips(s => !s)}
          >
            Strips
          </button>

          {!readonly && (
            <button
              className={styles.ghostBtn}
              onClick={handleSave}
              disabled={path.anchors.length < 2}
            >
              Save
            </button>
          )}

          {simResult && (
            <>
              <div className={styles.divider} />
              {terrain.status === 'loading' ? (
                <span className={styles.badge}>Preparing…</span>
              ) : (
                <button
                  className={styles.rideBtn}
                  onClick={() => setIsRideMode(true)}
                  disabled={terrain.status !== 'ready'}
                >
                  ▶ Ride
                </button>
              )}
            </>
          )}
        </div>
      )}

      {/* ── Simulation error ─────────────────────────────────────────────── */}
      {simError && <div className={styles.simError}>{simError}</div>}

      {/* ── Diagnostics strip ────────────────────────────────────────────── */}
      {diag && !simError && (
        <div className={styles.diagStrip}>
          <span>H: {diag.height_range_m[0].toFixed(0)}–{diag.height_range_m[1].toFixed(0)} m</span>
          <span>V: {diag.velocity_range_ms[0].toFixed(1)}–{diag.velocity_range_ms[1].toFixed(1)} m/s</span>
          {diag.g_force_range && (
            <span>G: {diag.g_force_range[0].toFixed(2)}–{diag.g_force_range[1].toFixed(2)} g</span>
          )}
          {diag.stall_at_pct !== null && (
            <span className={styles.diagWarn}>⚠ stall at {diag.stall_at_pct.toFixed(0)}%</span>
          )}
        </div>
      )}

      {/* ── Hint strip ──────────────────────────────────────────────────── */}
      {!selected && !diag && (
        <div className={styles.hint}>
          {path.mode === 'add'
            ? 'Left-click terrain to place track points · Right-click to undo'
            : path.mode === 'strip'
            ? 'Click path start of strip · Click path end · Right-click to cancel'
            : 'Left-click a point to select · Drag to reposition · Right-click to delete'}
        </div>
      )}

      {/* ── Selected-point panel ─────────────────────────────────────────── */}
      {selected && !readonly && (
        <div className={styles.selectedPanel}>
          <p className={styles.panelHeading}>
            Point {selectedIndex + 1} of {path.anchors.length}
          </p>
          <div className={styles.heightRow}>
            <span className={styles.heightLabel}>Height offset</span>
            <span className={styles.heightValue}>{selected.heightOffset.toFixed(1)} m</span>
          </div>
          {selectedIndex === 0 && (
            <div className={styles.heightRow}>
              <span className={styles.heightLabel}>Start velocity</span>
              <input
                type="number"
                min={0} max={100} step={0.5}
                value={initialVelocity}
                onChange={e => setInitialVelocity(Math.max(0, Math.min(100, Number(e.target.value))))}
                className={styles.velocityInput}
              />
              <span className={styles.heightLabel}>m/s</span>
            </div>
          )}
          <div className={styles.stepGroup}>
            <button className={`${styles.stepGroupBtn} ${styles.up}`}   onClick={() => path.updateAnchorHeight(selected.id,  5)}>+5 m</button>
            <button className={`${styles.stepGroupBtn} ${styles.up}`}   onClick={() => path.updateAnchorHeight(selected.id,  1)}>+1 m</button>
            <button className={`${styles.stepGroupBtn} ${styles.down}`} onClick={() => path.updateAnchorHeight(selected.id, -1)}>−1 m</button>
            <button className={`${styles.stepGroupBtn} ${styles.down}`} onClick={() => path.updateAnchorHeight(selected.id, -5)}>−5 m</button>
          </div>
          <button className={styles.removeBtn} onClick={() => path.removeAnchor(selected.id)}>
            Remove point
          </button>
        </div>
      )}

      {/* ── Simulation profile charts (left panel) ──────────────────────── */}
      {!isRideMode && simResult?.profile && (
        <SimulationPlots
          profile={simResult.profile}
          strips={accel.strips}
          rideCursor={rideCursor}
        />
      )}

      {/* ── Right panel column (acceleration strips) ─────────────────────── */}
      {!isRideMode && accel.strips.length > 0 && (
        <div className={styles.rightColumn}>
          <AccelerationStripsPanel
            strips={accel.strips}
            onRemoveStrip={accel.removeStrip}
            onUpdateStrip={accel.updateStrip}
            onUpdateStripFrac={accel.updateStripFrac}
          />
        </div>
      )}

      {/* ── Path hover tooltip ──────────────────────────────────────────── */}
      {!isRideMode && pathHover && (
        <div
          className={styles.pathTooltip}
          style={{ left: pathHover.x + 14, top: pathHover.y - 10 }}
        >
          {pathHover.pct.toFixed(1)}%
        </div>
      )}

      {/* ── Loading overlay ──────────────────────────────────────────────── */}
      {!challenge && <div className={styles.loading}>Loading challenge…</div>}
    </>
  )
}

// ── Small helper component ─────────────────────────────────────────────────────

function ModeButton({ label, active, onClick }: { label: string; active: boolean; onClick: () => void }) {
  return (
    <button className={`${styles.toolBtn}${active ? ` ${styles.active}` : ''}`} onClick={onClick}>
      {label}
    </button>
  )
}
