'use client'

import { useEffect, useMemo, useState } from 'react'
import { useBack } from '@/hooks/useBack'
import dynamic from 'next/dynamic'
import { useVisitor } from '@/context/VisitorContext'
import { t, tPlain } from '@/lib/i18n'
import type { SectionDTO, GeoPointDTO, GuidedPathDTO, GuidedStepDTO } from '@/lib/api/types'
import { useGeolocation } from '@/hooks/useGeolocation'
import { haversineMeters, formatDistance } from '@/lib/geo'
import { trackEvent } from '@/lib/stats'
import StepTimer from './map/StepTimer'
import StepQuiz from './map/StepQuiz'
import Toast from './map/Toast'
import './map/map.css'

const LeafletMap = dynamic(() => import('./map/LeafletMap'), {
  ssr: false,
  loading: () => (
    <div className="w-full h-full flex items-center justify-center" style={{ background: '#e8eef3' }}>
      <div className="text-sm" style={{ color: 'var(--color-text-muted)' }}>Chargement de la carte…</div>
    </div>
  ),
})

interface Props {
  section: SectionDTO
  slug: string
  configId: string
  languages: string[]
}

type Mode = 'map' | 'list'

export default function MapSection({ section, configId, languages }: Props) {
  const { language, setAvailableLanguages, instanceId } = useVisitor()
  const back = useBack()

  useEffect(() => { setAvailableLanguages([]) }, [languages])

  const map = section.map
  const hasData = !!map && (map.points?.length ?? 0) > 0
  const points = useMemo(() => map?.points ?? [], [map])
  const categories = useMemo(() => map?.categories ?? [], [map])
  const guidedPaths = useMemo(() => map?.guidedPaths ?? [], [map])

  const [mode, setMode] = useState<Mode>('map')
  const [selectedId, setSelectedId] = useState<number | null>(null)
  const [search, setSearch] = useState('')
  const [activeCats, setActiveCats] = useState<Set<number>>(new Set())
  const [filterOpen, setFilterOpen] = useState(false)
  const [pathListOpen, setPathListOpen] = useState(false)
  const [activePathId, setActivePathId] = useState<string | null>(null)
  const [activeStepId, setActiveStepId] = useState<string | null>(null)

  const activePath = guidedPaths.find((p) => p.id === activePathId) ?? null
  const activeSteps = useMemo(
    () => [...(activePath?.steps ?? [])].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)),
    [activePath]
  )
  const activeStep = activeSteps.find((s) => s.id === activeStepId) ?? null

  const [completedSteps, setCompletedSteps] = useState<Set<string>>(new Set())
  const [quizPassedSteps, setQuizPassedSteps] = useState<Set<string>>(new Set())
  const [timerExpiredSteps, setTimerExpiredSteps] = useState<Set<string>>(new Set())
  const [zoneNotifiedSteps, setZoneNotifiedSteps] = useState<Set<string>>(new Set())
  const [toastMessage, setToastMessage] = useState<string | null>(null)
  const [showEndModal, setShowEndModal] = useState(false)
  const [geoEnabledKey, setGeoEnabledKey] = useState(0)

  // Reset progression when path changes
  useEffect(() => {
    setCompletedSteps(new Set())
    setQuizPassedSteps(new Set())
    setTimerExpiredSteps(new Set())
    setZoneNotifiedSteps(new Set())
    setShowEndModal(false)
    setToastMessage(null)
  }, [activePathId])

  // First non-completed step in order = current
  const currentStepId = useMemo(() => {
    if (!activePath) return null
    const next = activeSteps.find((s) => !completedSteps.has(s.id))
    return next?.id ?? null
  }, [activePath, activeSteps, completedSteps])

  // Hide future steps if path requires it; also hide isHiddenInitially steps not yet current/completed
  const visibleSteps = useMemo(() => {
    if (!activePath) return []
    let list = activeSteps
    if (activePath.hideNextStepsUntilComplete) {
      const idx = currentStepId ? activeSteps.findIndex((s) => s.id === currentStepId) : activeSteps.length
      list = activeSteps.slice(0, idx + 1)
    }
    return list.filter((s) => {
      if (!s.isHiddenInitially) return true
      return completedSteps.has(s.id) || s.id === currentStepId
    })
  }, [activePath, activeSteps, currentStepId, completedSteps])

  function markStepComplete(stepId: string) {
    setCompletedSteps((prev) => {
      const next = new Set(prev)
      next.add(stepId)
      return next
    })
    const idx = activeSteps.findIndex((s) => s.id === stepId)
    if (idx === activeSteps.length - 1) {
      setShowEndModal(true)
      setActiveStepId(null)
    } else {
      const nextStep = activeSteps[idx + 1]
      if (nextStep) setActiveStepId(nextStep.id)
    }
  }

  const geo = useGeolocation(!!activePath, geoEnabledKey)

  // Toast on first entry into the current step's zone
  useEffect(() => {
    if (!activePath || !currentStepId) return
    const step = activeSteps.find((s) => s.id === currentStepId)
    if (!step) return
    const radius = step.zoneRadiusMeters ?? 0
    const coords = step.geometry?.coordinates
    if (radius <= 0 || !coords || geo.lat == null || geo.lng == null) return
    const dist = haversineMeters({ lat: geo.lat, lng: geo.lng }, { lat: coords[1], lng: coords[0] })
    if (dist <= radius && !zoneNotifiedSteps.has(currentStepId)) {
      const title = tPlain(step.title, language) || 'cette étape'
      setToastMessage(`Vous êtes arrivé à « ${title} »`)
      setZoneNotifiedSteps((prev) => {
        const next = new Set(prev)
        next.add(currentStepId)
        return next
      })
    }
  }, [activePath, currentStepId, activeSteps, geo.lat, geo.lng, zoneNotifiedSteps, language])

  const norm = (s: string) =>
    s.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()

  const filteredPoints = useMemo(() => {
    const q = norm(search.trim())
    return points.filter((p) => {
      if (activeCats.size > 0 && (p.categorieId == null || !activeCats.has(p.categorieId))) return false
      if (!q) return true
      const title = norm(tPlain(p.title, language))
      return title.includes(q)
    })
  }, [points, activeCats, search, language])

  const center: [number, number] = useMemo(() => {
    const lat = parseFloat(map?.centerLatitude ?? '')
    const lng = parseFloat(map?.centerLongitude ?? '')
    if (!isNaN(lat) && !isNaN(lng)) return [lat, lng]
    const first = points.find((p) => p.geometry?.coordinates)
    if (first?.geometry?.coordinates) {
      return [first.geometry.coordinates[1], first.geometry.coordinates[0]]
    }
    return [50.5, 4.5]
  }, [map, points])

  const zoom = map?.zoom ?? 14
  const selected = points.find((p) => p.id === selectedId) ?? null

  const [primaryColor, setPrimaryColor] = useState('#3a6ea5')
  useEffect(() => {
    const c = getComputedStyle(document.documentElement).getPropertyValue('--color-primary').trim()
    if (c) setPrimaryColor(c)
  }, [])

  function toggleCat(id: number) {
    setActiveCats((prev) => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })
  }

  if (!hasData) {
    return (
      <div style={{ position: 'fixed', inset: 0, background: 'var(--color-background)' }} className="flex flex-col">
        <div
          className="absolute top-0 left-0 right-0 flex items-center gap-2 px-3 py-2 z-[1000]"
          style={{ background: 'linear-gradient(135deg, var(--color-primary), var(--color-secondary))', color: 'var(--color-on-primary)' }}
        >
          <button onClick={back} className="w-10 h-10 rounded-full flex items-center justify-center" aria-label="Retour">
            <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor">
              <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
            </svg>
          </button>
          <span className="text-base font-semibold flex-1 truncate px-2">{tPlain(section.title, language)}</span>
        </div>
        <div className="flex-1 flex items-center justify-center text-sm text-center p-8" style={{ color: 'var(--color-text-muted)' }}>
          Aucun lieu à afficher sur la carte.
        </div>
      </div>
    )
  }

  return (
    <div style={{ position: 'fixed', inset: 0, background: '#e8eef3' }}>
      {/* Map / List */}
      {mode === 'map' ? (
        <LeafletMap
          points={filteredPoints}
          categories={categories}
          center={center}
          zoom={zoom}
          selectedId={selectedId}
          onSelect={(id) => {
            setSelectedId(id); setActiveStepId(null)
            const p = points.find((pp) => pp.id === id)
            if (instanceId && p) {
              trackEvent({
                instanceId, configurationId: configId, sectionId: section.id,
                eventType: 'MapPoiTap', language,
                metadata: JSON.stringify({ geoPointId: p.id, title: tPlain(p.title, language) }),
              })
            }
          }}
          primaryColor={primaryColor}
          pathSteps={visibleSteps}
          selectedStepId={activeStepId}
          onSelectStep={(id) => { setActiveStepId(id); setSelectedId(null) }}
          currentStepId={currentStepId}
          completedStepIds={completedSteps}
          userPosition={geo.lat != null && geo.lng != null ? { lat: geo.lat, lng: geo.lng } : null}
        />
      ) : (
        <PointList
          points={filteredPoints}
          language={language}
          categories={categories}
          onSelect={(id) => { setSelectedId(id); setMode('map') }}
        />
      )}

      {/* Top dark AppBar */}
      <div
        className="absolute top-0 left-0 right-0 flex items-center gap-2 px-3 py-2 z-[1000]"
        style={{
          background: 'linear-gradient(to bottom, rgba(0,0,0,0.55), rgba(0,0,0,0))',
          paddingTop: 'max(env(safe-area-inset-top), 10px)',
        }}
      >
        <button
          onClick={back}
          className="w-10 h-10 rounded-full flex items-center justify-center"
          style={{ background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(8px)' }}
          aria-label="Retour"
        >
          <svg width="22" height="22" viewBox="0 0 24 24" fill="white">
            <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
          </svg>
        </button>
        <span
          className="text-base font-semibold flex-1 truncate px-2"
          style={{ color: 'white', textShadow: '0 1px 4px rgba(0,0,0,0.6)' }}
        >
          {tPlain(section.title, language)}
        </span>
        {guidedPaths.length > 0 && (
          <button
            onClick={() => setPathListOpen(true)}
            className="h-10 rounded-full flex items-center gap-1.5 px-3 font-semibold text-xs"
            style={{
              background: activePath ? 'var(--color-primary)' : 'rgba(0,0,0,0.6)',
              color: 'white',
              backdropFilter: 'blur(8px)',
            }}
            aria-label="Parcours"
          >
            <svg width="18" height="18" viewBox="0 0 24 24" fill="white">
              <path d="M20 4h-4.18A2.99 2.99 0 0 0 13 2c-1.3 0-2.4.84-2.82 2H6c-1.1 0-2 .9-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6c0-1.1-.9-2-2-2zm-7 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm-2 14l-4-4 1.41-1.41L11 15.17l5.59-5.59L18 11l-7 7z" />
            </svg>
            {activePath ? `Étape ${(activeSteps.findIndex((s) => s.id === activeStepId) + 1) || 1}/${activeSteps.length}` : `Parcours (${guidedPaths.length})`}
          </button>
        )}
        <button
          onClick={() => setFilterOpen((v) => !v)}
          className="w-10 h-10 rounded-full flex items-center justify-center relative"
          style={{ background: filterOpen ? 'var(--color-primary)' : 'rgba(0,0,0,0.6)', backdropFilter: 'blur(8px)' }}
          aria-label="Filtres"
        >
          <svg width="22" height="22" viewBox="0 0 24 24" fill="white">
            <path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" />
          </svg>
          {activeCats.size > 0 && (
            <span
              className="absolute top-0 right-0 w-5 h-5 rounded-full text-[10px] font-bold flex items-center justify-center"
              style={{ background: 'white', color: 'var(--color-primary)' }}
            >
              {activeCats.size}
            </span>
          )}
        </button>
      </div>

      {/* Filter panel */}
      {filterOpen && (
        <div
          className="absolute top-16 left-3 right-3 rounded-2xl p-3 z-[1000] flex flex-col gap-3"
          style={{
            background: 'rgba(255,255,255,0.97)',
            backdropFilter: 'blur(12px)',
            boxShadow: '0 8px 24px rgba(0,0,0,0.18)',
            maxHeight: 'calc(100vh - 96px)',
          }}
        >
          <div className="flex items-center gap-2 px-3 py-2 rounded-full" style={{ background: '#f1f3f5' }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="#666">
              <path d="M15.5 14h-.79l-.28-.27A6.5 6.5 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" />
            </svg>
            <input
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              placeholder="Rechercher un lieu…"
              className="flex-1 bg-transparent text-sm outline-none"
              style={{ color: '#1a1a1a' }}
            />
            {search && (
              <button onClick={() => setSearch('')} aria-label="Effacer">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="#999">
                  <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
                </svg>
              </button>
            )}
          </div>

          {categories.length > 0 && (
            <div className="flex flex-wrap gap-2 overflow-y-auto" style={{ maxHeight: 260 }}>
              {categories.map((cat) => {
                const active = activeCats.has(cat.id)
                const color = cat.color || primaryColor
                return (
                  <button
                    key={cat.id}
                    onClick={() => toggleCat(cat.id)}
                    className="px-3 py-1.5 rounded-full text-xs font-medium flex items-center gap-1.5 transition-all"
                    style={{
                      background: active ? color : 'white',
                      color: active ? 'white' : '#333',
                      border: `1.5px solid ${color}`,
                    }}
                  >
                    <span
                      className="w-2 h-2 rounded-full"
                      style={{ background: active ? 'white' : color }}
                    />
                    {tPlain(cat.label, language) || `Cat. ${cat.id}`}
                  </button>
                )
              })}
            </div>
          )}

          <div className="flex items-center justify-between text-xs" style={{ color: '#666' }}>
            <span>{filteredPoints.length} lieu{filteredPoints.length > 1 ? 'x' : ''}</span>
            {(activeCats.size > 0 || search) && (
              <button
                onClick={() => { setActiveCats(new Set()); setSearch('') }}
                className="font-medium"
                style={{ color: 'var(--color-primary)' }}
              >
                Réinitialiser
              </button>
            )}
          </div>
        </div>
      )}

      {/* Bottom-right toggle */}
      {map?.isListViewEnabled && (
        <button
          onClick={() => setMode((m) => (m === 'map' ? 'list' : 'map'))}
          className="absolute bottom-6 right-4 rounded-full flex items-center gap-2 px-4 py-3 font-semibold text-sm z-[1000]"
          style={{
            background: 'var(--color-primary)',
            color: 'var(--color-on-primary)',
            boxShadow: '0 4px 12px rgba(0,0,0,0.25)',
          }}
        >
          {mode === 'map' ? (
            <>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
                <path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z" />
              </svg>
              Liste
            </>
          ) : (
            <>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
                <path d="M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25.36-.48V3.5c0-.28-.22-.5-.5-.5zM15 19l-6-2.11V5l6 2.11V19z" />
              </svg>
              Carte
            </>
          )}
        </button>
      )}

      {/* Quitter parcours button (when active) */}
      {activePath && !pathListOpen && !activeStep && !selected && (
        <button
          onClick={() => { setActivePathId(null); setActiveStepId(null) }}
          className="absolute bottom-6 left-4 rounded-full flex items-center gap-2 px-4 py-3 font-semibold text-sm z-[1000]"
          style={{
            background: 'var(--color-surface)',
            color: 'var(--color-text)',
            boxShadow: '0 4px 12px rgba(0,0,0,0.25)',
            border: '1px solid var(--color-border)',
          }}
        >
          <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
            <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
          </svg>
          Quitter le parcours
        </button>
      )}

      {/* Detail sheet (POI) */}
      {selected && (
        <PointDetail point={selected} language={language} onClose={() => setSelectedId(null)} />
      )}

      {/* Step detail sheet (parcours) */}
      {activeStep && activePath && (
        <StepDetail
          step={activeStep}
          stepIndex={activeSteps.findIndex((s) => s.id === activeStep.id)}
          totalSteps={activeSteps.length}
          language={language}
          geo={geo}
          isCurrent={activeStep.id === currentStepId}
          isCompleted={completedSteps.has(activeStep.id)}
          quizPassed={quizPassedSteps.has(activeStep.id)}
          timerExpired={timerExpiredSteps.has(activeStep.id)}
          requireSuccess={!!activePath.requireSuccessToAdvance}
          isLinear={!!activePath.isLinear}
          onQuizResult={(passed) => {
            if (passed) {
              setQuizPassedSteps((prev) => {
                const next = new Set(prev)
                next.add(activeStep.id)
                return next
              })
            }
          }}
          onTimerExpired={() => {
            setTimerExpiredSteps((prev) => {
              const next = new Set(prev)
              next.add(activeStep.id)
              return next
            })
          }}
          onRetryGeo={() => setGeoEnabledKey((k) => k + 1)}
          onClose={() => setActiveStepId(null)}
          onMarkComplete={() => markStepComplete(activeStep.id)}
          onPrev={() => {
            const i = activeSteps.findIndex((s) => s.id === activeStep.id)
            if (i > 0) setActiveStepId(activeSteps[i - 1].id)
          }}
          onNext={() => {
            const i = activeSteps.findIndex((s) => s.id === activeStep.id)
            if (i < activeSteps.length - 1) setActiveStepId(activeSteps[i + 1].id)
          }}
        />
      )}

      {/* Toast (entry into zone) */}
      {toastMessage && (
        <Toast message={toastMessage} onDismiss={() => setToastMessage(null)} />
      )}

      {/* End of path modal */}
      {showEndModal && activePath && (
        <EndOfPathModal
          pathTitle={tPlain(activePath.title, language) || 'Parcours'}
          stepCount={activeSteps.length}
          onRestart={() => {
            setCompletedSteps(new Set())
            setShowEndModal(false)
            if (activeSteps[0]) setActiveStepId(activeSteps[0].id)
          }}
          onLeave={() => {
            setActivePathId(null)
            setActiveStepId(null)
            setShowEndModal(false)
          }}
        />
      )}

      {/* Path list bottom sheet */}
      {pathListOpen && (
        <PathListSheet
          paths={guidedPaths}
          activePathId={activePathId}
          language={language}
          onSelect={(id) => {
            setActivePathId(id)
            setActiveStepId(null)
            setSelectedId(null)
            setPathListOpen(false)
          }}
          onLeave={() => {
            setActivePathId(null)
            setActiveStepId(null)
            setPathListOpen(false)
          }}
          onClose={() => setPathListOpen(false)}
        />
      )}
    </div>
  )
}

// ── Path list bottom sheet ──────────────────────────────────────────────────
function PathListSheet({
  paths, activePathId, language, onSelect, onLeave, onClose,
}: {
  paths: GuidedPathDTO[]
  activePathId: string | null
  language: string
  onSelect: (id: string) => void
  onLeave: () => void
  onClose: () => void
}) {
  return (
    <>
      <div className="absolute inset-0 z-[1500]" style={{ background: 'rgba(0,0,0,0.3)' }} onClick={onClose} />
      <div
        className="absolute left-0 right-0 bottom-0 z-[1600] rounded-t-3xl overflow-hidden"
        style={{
          background: 'var(--color-surface)',
          maxHeight: '70%',
          boxShadow: '0 -8px 24px rgba(0,0,0,0.2)',
          animation: 'mim-slide-up 0.25s ease',
        }}
      >
        <style>{`@keyframes mim-slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }`}</style>
        <div className="flex justify-center pt-2 pb-1">
          <div style={{ width: 40, height: 4, borderRadius: 2, background: 'var(--color-border)' }} />
        </div>
        <div className="px-5 pt-2 pb-3 flex items-center justify-between">
          <div className="text-base font-bold" style={{ color: 'var(--color-text)' }}>Parcours</div>
          {activePathId && (
            <button onClick={onLeave} className="text-xs font-medium" style={{ color: 'var(--color-primary)' }}>
              Quitter le parcours
            </button>
          )}
        </div>
        <div className="overflow-y-auto px-3 pb-5 flex flex-col gap-2" style={{ maxHeight: 'calc(70vh - 80px)' }}>
          {paths.map((p) => {
            const active = p.id === activePathId
            const stepCount = p.steps?.length ?? 0
            return (
              <button
                key={p.id}
                onClick={() => onSelect(p.id)}
                className="flex items-center gap-3 p-3 rounded-2xl text-left transition-colors"
                style={{
                  background: active ? 'var(--color-primary-light)' : 'var(--color-surface)',
                  border: `1.5px solid ${active ? 'var(--color-primary)' : 'var(--color-border)'}`,
                }}
              >
                <div
                  className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
                  style={{ background: 'var(--color-primary)', color: 'var(--color-on-primary)' }}
                >
                  <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M12 2L4 5v6.09c0 5.05 3.41 9.76 8 10.91 4.59-1.15 8-5.86 8-10.91V5l-8-3z" />
                  </svg>
                </div>
                <div className="flex-1 min-w-0">
                  <div
                    className="text-sm font-semibold [&_p]:m-0"
                    style={{ color: 'var(--color-text)' }}
                    dangerouslySetInnerHTML={{ __html: t(p.title, language) || 'Parcours' }}
                  />
                  <div className="text-xs mt-0.5" style={{ color: 'var(--color-text-muted)' }}>
                    {stepCount} étape{stepCount > 1 ? 's' : ''}
                    {p.isLinear && ' · linéaire'}
                  </div>
                </div>
                <svg width="20" height="20" viewBox="0 0 24 24" fill="var(--color-text-muted)">
                  <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
                </svg>
              </button>
            )
          })}
        </div>
      </div>
    </>
  )
}

// ── Step detail bottom sheet ────────────────────────────────────────────────
function StepDetail({
  step, stepIndex, totalSteps, language, geo,
  isCurrent, isCompleted, quizPassed, timerExpired,
  requireSuccess, isLinear,
  onQuizResult, onTimerExpired, onRetryGeo,
  onClose, onMarkComplete, onPrev, onNext,
}: {
  step: GuidedStepDTO
  stepIndex: number
  totalSteps: number
  language: string
  geo: ReturnType<typeof useGeolocation>
  isCurrent: boolean
  isCompleted: boolean
  quizPassed: boolean
  timerExpired: boolean
  requireSuccess: boolean
  isLinear: boolean
  onQuizResult: (passed: boolean) => void
  onTimerExpired: () => void
  onRetryGeo: () => void
  onClose: () => void
  onMarkComplete: () => void
  onPrev: () => void
  onNext: () => void
}) {
  const hasPrev = stepIndex > 0 && !isLinear
  const hasNext = stepIndex < totalSteps - 1 && !isLinear

  const stepCoords = step.geometry?.coordinates
  const stepLat = stepCoords?.[1]
  const stepLng = stepCoords?.[0]
  const radius = step.zoneRadiusMeters ?? 0
  const distance = geo.lat != null && geo.lng != null && stepLat != null && stepLng != null
    ? haversineMeters({ lat: geo.lat, lng: geo.lng }, { lat: stepLat, lng: stepLng })
    : null
  const inZone = distance != null && radius > 0 && distance <= radius

  const hasQuiz = (step.quizQuestions?.length ?? 0) > 0
  const hasTimer = !!step.isStepTimer && (step.timerSeconds ?? 0) > 0
  const isLocked = !!step.isStepLocked
  const geoUnavailable = geo.status === 'denied' || geo.status === 'unavailable'

  // canAdvance composé :
  // - quiz : si présent + requireSuccess → doit être passé
  // - zone : si présente + requireSuccess → doit être dans la zone (sauf si GPS indispo)
  // - lock : si verrouillé + requireSuccess → doit avoir au moins une preuve (quiz ou zone)
  const quizOk = !hasQuiz || quizPassed || !requireSuccess
  const zoneOk = radius === 0 || inZone || !requireSuccess || geoUnavailable
  const lockOk = !isLocked || !requireSuccess || quizPassed || inZone || geoUnavailable
  const canAdvance = quizOk && zoneOk && lockOk
  return (
    <>
      <div className="absolute inset-0 z-[1500]" style={{ background: 'rgba(0,0,0,0.3)' }} onClick={onClose} />
      <div
        className="absolute left-0 right-0 bottom-0 z-[1600] rounded-t-3xl overflow-hidden"
        style={{
          background: 'var(--color-surface)',
          maxHeight: '78%',
          boxShadow: '0 -8px 24px rgba(0,0,0,0.2)',
          animation: 'mim-slide-up 0.25s ease',
        }}
      >
        <div className="relative" style={{ height: 160, background: 'var(--color-primary)' }}>
          {step.imageUrl && (
            // eslint-disable-next-line @next/next/no-img-element
            <img src={step.imageUrl} alt="" className="absolute inset-0 w-full h-full object-cover" />
          )}
          <div className="absolute inset-0" style={{ background: 'linear-gradient(to top, rgba(0,0,0,0.7), rgba(0,0,0,0.1))' }} />
          <button
            onClick={onClose}
            className="absolute top-3 right-3 w-9 h-9 rounded-full flex items-center justify-center"
            style={{ background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(6px)' }}
            aria-label="Fermer"
          >
            <svg width="20" height="20" viewBox="0 0 24 24" fill="white">
              <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
            </svg>
          </button>
          <div
            className="absolute top-3 left-3 px-2.5 py-1 rounded-full text-xs font-bold"
            style={{ background: 'rgba(255,255,255,0.95)', color: 'var(--color-primary)' }}
          >
            Étape {stepIndex + 1} / {totalSteps}
          </div>
          <div
            className="absolute bottom-3 left-4 right-12 text-white text-lg font-bold [&_p]:m-0"
            style={{ textShadow: '0 2px 6px rgba(0,0,0,0.6)' }}
            dangerouslySetInnerHTML={{ __html: t(step.title, language) || 'Étape' }}
          />
        </div>

        <div className="overflow-y-auto px-5 py-4" style={{ maxHeight: 'calc(78vh - 160px - 64px)' }}>
          {t(step.description, language) && (
            <div
              className="text-sm leading-relaxed [&_p]:m-0 [&_p+p]:mt-2"
              style={{ color: 'var(--color-text)' }}
              dangerouslySetInnerHTML={{ __html: t(step.description, language) }}
            />
          )}
          {radius > 0 && (
            <div className="mt-3 flex flex-wrap gap-2 items-center">
              <div
                className="inline-flex items-center gap-1.5 text-xs px-2.5 py-1 rounded-full"
                style={{
                  background: inZone ? '#d1fae5' : 'var(--color-primary-light)',
                  color: inZone ? '#065f46' : 'var(--color-primary)',
                }}
              >
                <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
                  <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z" />
                </svg>
                {inZone
                  ? 'Vous êtes dans la zone'
                  : distance != null
                    ? `À ${formatDistance(distance)} de la zone (${radius} m)`
                    : `Zone à atteindre · ${radius} m`}
              </div>
              {geo.status === 'requesting' && (
                <span className="text-xs" style={{ color: 'var(--color-text-muted)' }}>Localisation en cours…</span>
              )}
              {geo.status === 'denied' && (
                <button
                  onClick={onRetryGeo}
                  className="text-xs underline"
                  style={{ color: '#b91c1c' }}
                >
                  Localisation refusée — réessayer
                </button>
              )}
              {geo.status === 'unavailable' && (
                <span className="text-xs" style={{ color: 'var(--color-text-muted)' }}>GPS indisponible</span>
              )}
            </div>
          )}

          {isLocked && !isCompleted && (
            <div className="mt-3 inline-flex items-center gap-1.5 text-xs px-2.5 py-1 rounded-full" style={{ background: '#fef3c7', color: '#92400e' }}>
              <span>🔒</span>
              {quizPassed || inZone ? 'Étape déverrouillée' : 'Étape verrouillée — validez la condition'}
            </div>
          )}

          {hasTimer && isCurrent && !isCompleted && (
            <div className="mt-4">
              <StepTimer
                totalSeconds={step.timerSeconds!}
                active={!timerExpired}
                onExpired={onTimerExpired}
              />
              {timerExpired && t(step.timerExpiredMessage, language) && (
                <div
                  className="mt-2 text-xs p-2 rounded-lg [&_p]:m-0"
                  style={{ background: '#fee2e2', color: '#991b1b' }}
                  dangerouslySetInnerHTML={{ __html: t(step.timerExpiredMessage, language) }}
                />
              )}
            </div>
          )}

          {hasQuiz && isCurrent && !isCompleted && (
            <div className="mt-4">
              <div className="text-xs font-semibold mb-2 uppercase tracking-wide" style={{ color: 'var(--color-text-muted)' }}>
                Défi
              </div>
              <StepQuiz
                key={step.id}
                questions={step.quizQuestions!}
                language={language}
                onResult={onQuizResult}
              />
              {quizPassed && (
                <div className="mt-2 text-xs text-center py-1.5 rounded-full" style={{ background: '#d1fae5', color: '#065f46' }}>
                  ✓ Défi réussi
                </div>
              )}
            </div>
          )}
        </div>

        <div className="flex flex-col gap-2 px-4 pb-4 pt-2 border-t" style={{ borderColor: 'var(--color-border)' }}>
          {isCompleted && (
            <div className="text-xs text-center py-1.5 rounded-full" style={{ background: '#d1fae5', color: '#065f46' }}>
              ✓ Étape terminée
            </div>
          )}
          {isCurrent && !isCompleted && (
            <button
              onClick={onMarkComplete}
              disabled={!canAdvance}
              className="w-full py-3 rounded-2xl font-semibold text-sm"
              style={{
                background: canAdvance ? 'var(--color-primary)' : 'var(--color-surface)',
                color: canAdvance ? 'var(--color-on-primary)' : 'var(--color-text-muted)',
                border: canAdvance ? 'none' : '1px solid var(--color-border)',
                cursor: canAdvance ? 'pointer' : 'not-allowed',
              }}
              title={!canAdvance ? 'Approchez de la zone pour valider' : undefined}
            >
              {stepIndex === totalSteps - 1 ? 'Terminer le parcours' : 'Marquer terminée'}
            </button>
          )}
          {!isLinear && (
            <div className="flex items-center justify-between gap-2">
              <button
                onClick={onPrev}
                disabled={!hasPrev}
                className="flex-1 py-2.5 rounded-xl text-sm font-semibold flex items-center justify-center gap-1"
                style={{
                  background: 'var(--color-surface)',
                  color: 'var(--color-text)',
                  border: '1px solid var(--color-border)',
                  opacity: hasPrev ? 1 : 0.4,
                }}
              >
                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" /></svg>
                Précédente
              </button>
              <button
                onClick={onNext}
                disabled={!hasNext}
                className="flex-1 py-2.5 rounded-xl text-sm font-semibold flex items-center justify-center gap-1"
                style={{
                  background: 'var(--color-surface)',
                  color: 'var(--color-text)',
                  border: '1px solid var(--color-border)',
                  opacity: hasNext ? 1 : 0.4,
                }}
              >
                Suivante
                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" /></svg>
              </button>
            </div>
          )}
        </div>
      </div>
    </>
  )
}

// ── Point list view ─────────────────────────────────────────────────────────
function PointList({
  points, language, categories, onSelect,
}: {
  points: GeoPointDTO[]
  language: string
  categories: { id: number; color?: string }[]
  onSelect: (id: number) => void
}) {
  const colorOf = (id?: number) => {
    if (id == null) return 'var(--color-primary)'
    return categories.find((c) => c.id === id)?.color || 'var(--color-primary)'
  }
  return (
    <div className="absolute inset-0 overflow-y-auto pt-20 pb-24 px-3" style={{ background: 'var(--color-background)' }}>
      <div className="flex flex-col gap-2">
        {points.length === 0 && (
          <div className="text-center text-sm py-12" style={{ color: 'var(--color-text-muted)' }}>
            Aucun lieu à afficher.
          </div>
        )}
        {points.map((p) => (
          <button
            key={p.id}
            onClick={() => onSelect(p.id)}
            className="flex items-center gap-3 p-3 rounded-2xl text-left"
            style={{ background: 'var(--color-surface)', border: '1px solid var(--color-border)' }}
          >
            {p.imageUrl ? (
              // eslint-disable-next-line @next/next/no-img-element
              <img src={p.imageUrl} alt="" className="w-14 h-14 rounded-xl object-cover flex-shrink-0" />
            ) : (
              <div
                className="w-14 h-14 rounded-xl flex items-center justify-center flex-shrink-0"
                style={{ background: colorOf(p.categorieId) }}
              >
                <svg width="22" height="22" viewBox="0 0 24 24" fill="white">
                  <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z" />
                </svg>
              </div>
            )}
            <div className="flex-1 min-w-0">
              <div
                className="text-sm font-semibold truncate [&_p]:m-0"
                style={{ color: 'var(--color-text)' }}
                dangerouslySetInnerHTML={{ __html: t(p.title, language) }}
              />
              <div
                className="text-xs line-clamp-2 [&_p]:m-0 mt-0.5"
                style={{ color: 'var(--color-text-muted)' }}
                dangerouslySetInnerHTML={{ __html: t(p.description, language) }}
              />
            </div>
          </button>
        ))}
      </div>
    </div>
  )
}

// ── Detail sheet ────────────────────────────────────────────────────────────
function PointDetail({
  point, language, onClose,
}: {
  point: GeoPointDTO
  language: string
  onClose: () => void
}) {
  const phone = tPlain(point.phone, language)
  const email = tPlain(point.email, language)
  const site = tPlain(point.site, language)
  const prices = t(point.prices, language)
  const schedules = t(point.schedules, language)

  return (
    <>
      <div
        className="absolute inset-0 z-[1500]"
        style={{ background: 'rgba(0,0,0,0.3)' }}
        onClick={onClose}
      />
      <div
        className="absolute left-0 right-0 bottom-0 z-[1600] rounded-t-3xl overflow-hidden"
        style={{
          background: 'var(--color-surface)',
          maxHeight: '80%',
          boxShadow: '0 -8px 24px rgba(0,0,0,0.2)',
          animation: 'mim-slide-up 0.25s ease',
        }}
      >
        <style>{`@keyframes mim-slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }`}</style>

        {/* Header image */}
        <div className="relative" style={{ height: 180, background: 'var(--color-primary)' }}>
          {point.imageUrl && (
            // eslint-disable-next-line @next/next/no-img-element
            <img src={point.imageUrl} alt="" className="absolute inset-0 w-full h-full object-cover" />
          )}
          <div className="absolute inset-0" style={{ background: 'linear-gradient(to top, rgba(0,0,0,0.65), rgba(0,0,0,0.1))' }} />
          <button
            onClick={onClose}
            className="absolute top-3 right-3 w-9 h-9 rounded-full flex items-center justify-center"
            style={{ background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(6px)' }}
            aria-label="Fermer"
          >
            <svg width="20" height="20" viewBox="0 0 24 24" fill="white">
              <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
            </svg>
          </button>
          <div
            className="absolute bottom-3 left-4 right-12 text-white text-xl font-bold [&_p]:m-0"
            style={{ textShadow: '0 2px 6px rgba(0,0,0,0.6)' }}
            dangerouslySetInnerHTML={{ __html: t(point.title, language) }}
          />
        </div>

        {/* Body */}
        <div className="overflow-y-auto px-5 py-4 flex flex-col gap-4" style={{ maxHeight: 'calc(80vh - 180px)' }}>
          {t(point.description, language) && (
            <div
              className="text-sm leading-relaxed [&_p]:m-0 [&_p+p]:mt-2"
              style={{ color: 'var(--color-text)' }}
              dangerouslySetInnerHTML={{ __html: t(point.description, language) }}
            />
          )}

          {schedules && (
            <InfoBlock icon="schedule" label="Horaires" html={schedules} />
          )}
          {prices && (
            <InfoBlock icon="euro" label="Tarifs" html={prices} />
          )}

          {(phone || email || site) && (
            <div className="flex flex-col gap-2 pt-2 border-t" style={{ borderColor: 'var(--color-border)' }}>
              {phone && (
                <a href={`tel:${phone}`} className="flex items-center gap-3 text-sm" style={{ color: 'var(--color-primary)' }}>
                  <Icon name="phone" />
                  <span>{phone}</span>
                </a>
              )}
              {email && (
                <a href={`mailto:${email}`} className="flex items-center gap-3 text-sm" style={{ color: 'var(--color-primary)' }}>
                  <Icon name="email" />
                  <span className="truncate">{email}</span>
                </a>
              )}
              {site && (
                <a
                  href={site.startsWith('http') ? site : `https://${site}`}
                  target="_blank"
                  rel="noreferrer"
                  className="flex items-center gap-3 text-sm"
                  style={{ color: 'var(--color-primary)' }}
                >
                  <Icon name="web" />
                  <span className="truncate">{site}</span>
                </a>
              )}
            </div>
          )}
        </div>
      </div>
    </>
  )
}

function InfoBlock({ icon, label, html }: { icon: 'schedule' | 'euro'; label: string; html: string }) {
  return (
    <div className="flex gap-3">
      <div className="w-9 h-9 rounded-full flex items-center justify-center flex-shrink-0" style={{ background: 'var(--color-primary-light)' }}>
        <Icon name={icon} />
      </div>
      <div className="flex-1">
        <div className="text-xs font-semibold uppercase tracking-wide mb-1" style={{ color: 'var(--color-text-muted)' }}>{label}</div>
        <div
          className="text-sm [&_p]:m-0 [&_p+p]:mt-1"
          style={{ color: 'var(--color-text)' }}
          dangerouslySetInnerHTML={{ __html: html }}
        />
      </div>
    </div>
  )
}

function Icon({ name }: { name: 'phone' | 'email' | 'web' | 'schedule' | 'euro' }) {
  const paths: Record<string, string> = {
    phone: 'M6.62 10.79a15.05 15.05 0 0 0 6.59 6.59l2.2-2.2a1 1 0 0 1 1.05-.24c1.12.37 2.33.57 3.57.57a1 1 0 0 1 1 1V20a1 1 0 0 1-1 1A17 17 0 0 1 3 4a1 1 0 0 1 1-1h3.5a1 1 0 0 1 1 1c0 1.25.2 2.45.57 3.57a1 1 0 0 1-.25 1.05l-2.2 2.17z',
    email: 'M20 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z',
    web: 'M12 2A10 10 0 1 0 22 12 10 10 0 0 0 12 2zm6.93 6h-2.95a15.65 15.65 0 0 0-1.38-3.56A8 8 0 0 1 18.92 8zM12 4a14 14 0 0 1 1.81 4h-3.62A14 14 0 0 1 12 4zM4.26 14a8 8 0 0 1 0-4h3.38a16.5 16.5 0 0 0 0 4zm.82 2h2.95a15.65 15.65 0 0 0 1.38 3.56A8 8 0 0 1 5.08 16zm2.95-8H5.08a8 8 0 0 1 4.33-3.56A15.65 15.65 0 0 0 8.05 8zM12 20a14 14 0 0 1-1.81-4h3.62A14 14 0 0 1 12 20zm2.34-6h-4.68a14.6 14.6 0 0 1 0-4h4.68a14.6 14.6 0 0 1 0 4zm.25 5.56A15.65 15.65 0 0 0 15.97 16h2.95a8 8 0 0 1-4.33 3.56zM16.36 14a16.5 16.5 0 0 0 0-4h3.38a8 8 0 0 1 0 4z',
    schedule: 'M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z',
    euro: 'M15 18.5A6.48 6.48 0 0 1 9.24 15H15v-2H8.58a6.6 6.6 0 0 1 0-2H15V9H9.24A6.49 6.49 0 0 1 15 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.96 8.96 0 0 0 15 3a9 9 0 0 0-8.55 6H3v2h2.05c-.03.33-.05.66-.05 1s.02.67.05 1H3v2h3.45a9 9 0 0 0 8.55 6c2.3 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z',
  }
  return (
    <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
      <path d={paths[name]} />
    </svg>
  )
}

// ── End of path modal ──────────────────────────────────────────────────────
function EndOfPathModal({
  pathTitle, stepCount, onRestart, onLeave,
}: {
  pathTitle: string
  stepCount: number
  onRestart: () => void
  onLeave: () => void
}) {
  return (
    <div className="absolute inset-0 z-[2000] flex items-center justify-center p-6" style={{ background: 'rgba(0,0,0,0.55)' }}>
      <div
        className="w-full max-w-md rounded-3xl overflow-hidden"
        style={{
          background: 'var(--color-surface)',
          boxShadow: '0 20px 50px rgba(0,0,0,0.4)',
          animation: 'mim-pop-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)',
        }}
      >
        <style>{`@keyframes mim-pop-in { from { transform: scale(0.85); opacity: 0; } to { transform: scale(1); opacity: 1; } }`}</style>
        <div className="px-6 pt-6 pb-2 flex flex-col items-center gap-3">
          <div
            className="w-20 h-20 rounded-full flex items-center justify-center"
            style={{ background: '#d1fae5' }}
          >
            <svg width="44" height="44" viewBox="0 0 24 24" fill="#16a34a">
              <path d="M12 2L4 5v6.09c0 5.05 3.41 9.76 8 10.91 4.59-1.15 8-5.86 8-10.91V5l-8-3zm-2 14l-4-4 1.41-1.41L10 13.17l6.59-6.59L18 8l-8 8z" />
            </svg>
          </div>
          <div className="text-xl font-bold text-center" style={{ color: 'var(--color-text)' }}>Bravo !</div>
          <div className="text-sm text-center" style={{ color: 'var(--color-text-muted)' }}>
            Tu as terminé le parcours « {pathTitle} » ({stepCount} étape{stepCount > 1 ? 's' : ''}).
          </div>
        </div>
        <div className="flex flex-col gap-2 px-5 pb-5 pt-3">
          <button
            onClick={onLeave}
            className="w-full py-3 rounded-2xl font-semibold text-sm"
            style={{ background: 'var(--color-primary)', color: 'var(--color-on-primary)' }}
          >
            Retour à la carte
          </button>
          <button
            onClick={onRestart}
            className="w-full py-3 rounded-2xl font-semibold text-sm"
            style={{
              background: 'var(--color-surface)',
              color: 'var(--color-text)',
              border: '1px solid var(--color-border)',
            }}
          >
            Recommencer
          </button>
        </div>
      </div>
    </div>
  )
}
