<script>
  import { onMount, onDestroy } from 'svelte'
  import { satPositions, trackedSat, satList, watchlist, settings, passes } from '../stores/satstore.js'
  import { Go } from '../lib/wails.js'
  import { formatRange } from '../lib/utils.js'
  import 'leaflet/dist/leaflet.css'

  let mapEl, map, L
  let markers = {}
  let footprintCircle = null
  let groundtrackLayer = null
  let popupData = null
  let currentTracked = ''
  let popupX = null
  let popupY = null
  let dragging = false
  let dragOffX = 0, dragOffY = 0

  function startDrag(e) {
    dragging = true
    dragOffX = e.clientX - (popupX ?? 0)
    dragOffY = e.clientY - (popupY ?? 0)
    e.preventDefault()
  }
  function onDrag(e) {
    if (!dragging) return
    popupX = e.clientX - dragOffX
    popupY = e.clientY - dragOffY
  }
  function stopDrag() { dragging = false }

  // Reactive tick — updates every second, drives countdown in template
  let now = Date.now()
  setInterval(() => { now = Date.now() }, 1000)

  function fmtPassTime(isoStr) {
    if (!isoStr) return '—'
    return new Date(isoStr).toUTCString().slice(17, 25) + ' UTC'
  }

  // Reactive pass info — recomputes whenever now or $passes changes
  $: passInfo = (() => {
    const ps = $passes || []
    for (const p of ps) {
      const aos = new Date(p.aos).getTime()
      const los = new Date(p.los).getTime()
      if (now >= aos && now <= los) return { aos: p.aos, los: p.los, active: true, maxEl: p.maxEl }
      if (aos > now) return { aos: p.aos, los: p.los, active: false, maxEl: p.maxEl }
    }
    return null
  })()

  // Reactive countdown string — recomputes every second
  $: countdownStr = (() => {
    if (!passInfo || passInfo.active) return ''
    const diff = Math.floor((new Date(passInfo.aos).getTime() - now) / 1000)
    if (diff <= 0) return ''
    const m = Math.floor(diff / 60), s = diff % 60
    return `in ${m}m ${String(s).padStart(2,'0')}s`
  })()

  $: countdownLOS = (() => {
    if (!passInfo?.active) return ''
    const diff = Math.floor((new Date(passInfo.los).getTime() - now) / 1000)
    if (diff <= 0) return ''
    const m = Math.floor(diff / 60), s = diff % 60
    return `${m}m ${String(s).padStart(2,'0')}s`
  })()
  let cleanups = []

  // Watchlist panel
  let showWatchlistPanel = false
  let wlSearch = ''
  let wlSelected = []

  onMount(async () => {
    L = (await import('leaflet')).default

    // Map is ALWAYS visible (z-index approach in App.svelte)
    // So Leaflet can measure container correctly on first render
    map = L.map(mapEl, {
      center: [15, 0], zoom: 2,
      zoomControl: false, attributionControl: true,
      minZoom: 2, maxZoom: 8,
      maxBounds: [[-90, -200], [90, 200]],
      maxBoundsViscosity: 1.0,
      worldCopyJump: false,
    })

    // NASA GIBS Blue Marble - satellite imagery like SatTrack
    L.tileLayer('https://map1.vis.earthdata.nasa.gov/wmts-webmerc/BlueMarble_NextGeneration/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg', {
      attribution: 'Imagery © NASA EOSDIS GIBS',
      maxZoom: 8, noWrap: true, tileSize: 256,
    }).addTo(map)

    // Fill width: zoom so 360° longitude exactly fills container width
    const fitWidth = () => {
      const el = mapEl
      if (!el || el.clientWidth === 0) return
      map.invalidateSize({ animate: false, pan: false })
      // At tile size 256: world_px = 256 * 2^z  => z = log2(width / 256)
      const z = Math.log2(el.clientWidth / 256)
      map.setView([15, 0], z, { animate: false })
      map.setMinZoom(z - 0.1)
    }

    requestAnimationFrame(() => {
      fitWidth()
      setTimeout(fitWidth, 200)
      setTimeout(fitWidth, 500)
    })

    const ro = new ResizeObserver(() => {
      fitWidth()
      setTimeout(fitWidth, 200)
    })
    ro.observe(mapEl)
    cleanups.push(() => ro.disconnect())

    // QTH marker
    const qthIcon = L.divIcon({
      html: `<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg">
        <line x1="7" y1="1" x2="7" y2="13" stroke="#cc2020" stroke-width="2.5" stroke-linecap="round"/>
        <line x1="1" y1="7" x2="13" y2="7" stroke="#cc2020" stroke-width="2.5" stroke-linecap="round"/>
      </svg>`,
      className: '', iconSize:[14,14], iconAnchor:[7,7]
    })
    let qthMarker = L.marker([$settings.qthLat || 48.7, $settings.qthLon || 2.55], {
      icon: qthIcon, zIndexOffset: 1000
    }).addTo(map)
    cleanups.push(settings.subscribe(s => {
      if (s.qthLat && s.qthLon) qthMarker.setLatLng([s.qthLat, s.qthLon])
    }))

    cleanups.push(satPositions.subscribe(p => updateMarkers(p)))

    cleanups.push(trackedSat.subscribe(async name => {
      Object.values(markers).forEach(m => m.remove())
      markers = {}
      footprintCircle?.remove(); footprintCircle = null
      if (footprintSvgLayer) { map.removeLayer(footprintSvgLayer); footprintSvgLayer = null }
      groundtrackLayer?.remove(); groundtrackLayer = null
      popupData = null
      currentTracked = name
      if (name) {
        const track = await Go.GetGroundtrack(name, 100)
        if (track?.length > 1) drawGroundtrack(track)
      }
    }))

    cleanups.push(watchlist.subscribe(() => {
      Object.values(markers).forEach(m => m.remove())
      markers = {}
      footprintCircle?.remove(); footprintCircle = null
    }))

    map.on('click', () => { popupData = null })
  })

  onDestroy(() => { cleanups.forEach(fn => fn?.()); map?.remove() })

  const SAT_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="28" height="28">
    <rect x="0" y="12" width="9" height="8" rx="1" fill="#5ba3e0" stroke="white" stroke-width="1"/>
    <line x1="3" y1="12" x2="3" y2="20" stroke="white" stroke-width="0.5" opacity="0.6"/>
    <line x1="6" y1="12" x2="6" y2="20" stroke="white" stroke-width="0.5" opacity="0.6"/>
    <rect x="23" y="12" width="9" height="8" rx="1" fill="#5ba3e0" stroke="white" stroke-width="1"/>
    <line x1="26" y1="12" x2="26" y2="20" stroke="white" stroke-width="0.5" opacity="0.6"/>
    <line x1="29" y1="12" x2="29" y2="20" stroke="white" stroke-width="0.5" opacity="0.6"/>
    <rect x="10" y="10" width="12" height="12" rx="2" fill="#1a5a9a" stroke="white" stroke-width="1.5"/>
    <line x1="16" y1="10" x2="16" y2="5" stroke="white" stroke-width="1.2"/>
    <circle cx="16" cy="4" r="1.5" fill="white"/>
    <circle cx="16" cy="16" r="2" fill="white" opacity="0.9"/>
    <circle cx="16" cy="16" r="14" fill="none" stroke="#40c8f0" stroke-width="1" opacity="0.5" stroke-dasharray="3,3"/>
  </svg>`

  function makeIcon(name, isTracked, el) {
    const above = el >= 0
    const color = isTracked ? '#1a5a9a' : above ? '#2563eb' : '#9ca3af'
    const size  = isTracked ? 14 : above ? 10 : 6
    const border = `2px solid white`
    const shadow = isTracked ? `box-shadow:0 0 0 2px rgba(26,90,154,0.4),0 2px 4px rgba(0,0,0,0.2)`
                 : above     ? `box-shadow:0 0 0 1px rgba(37,99,235,0.3),0 1px 3px rgba(0,0,0,0.2)` 
                             : `box-shadow:0 1px 2px rgba(0,0,0,0.15)`
    const label = isTracked
      ? `<span class="slabel">${name.replace(' (ZARYA)','')}</span>` : ''
    if (isTracked) {
      return L.divIcon({
        html: `<div class="sw sw-sat">${SAT_SVG}${label}</div>`,
        className:'', iconSize:[0,0], iconAnchor:[14,14],
      })
    }
    return L.divIcon({
      html: `<div class="sw"><div class="sd" style="width:${size}px;height:${size}px;background:${color};border:${border};border-radius:50%;${shadow}"></div>${label}</div>`,
      className:'', iconSize:[0,0], iconAnchor:[0,0],
    })
  }

  function updateMarkers(positions) {
    const wl = $watchlist
    const filtered = wl.length > 0 ? positions.filter(p => wl.some(w => p.name.toUpperCase() === w.toUpperCase())) : positions
    const seen = new Set()
    filtered.forEach(pos => {
      seen.add(pos.name)
      const isTracked = pos.name === currentTracked
      if (markers[pos.name]) {
        markers[pos.name].setLatLng([pos.lat, pos.lon])
        markers[pos.name].setIcon(makeIcon(pos.name, isTracked, pos.el))
        markers[pos.name]._pos = pos
      } else {
        const m = L.marker([pos.lat, pos.lon], { icon: makeIcon(pos.name, isTracked, pos.el), zIndexOffset: isTracked ? 999 : 0 }).addTo(map)
        m._pos = pos
m.on('click', e => { L.DomEvent.stopPropagation(e); popupData = { ...pos }; trackedSat.set(pos.name) })
        markers[pos.name] = m
      }
      if (popupData?.name === pos.name) popupData = { ...pos }
      if (isTracked) {
        footprintCircle?.remove(); footprintCircle = null
        drawFootprintSVG(pos.lat, pos.lon, pos.footprint)
      }
    })
    Object.keys(markers).forEach(n => { if (!seen.has(n)) { markers[n].remove(); delete markers[n] } })
  }

  // Footprint — split polylines at antimeridian and polar boundaries
  let footprintSvgLayer = null

  function geoCircleRaw(lat, lon, radiusKm, n = 360) {
    const R = 6371.0, d = radiusKm / R
    const lat0 = lat * Math.PI / 180, lon0 = lon * Math.PI / 180
    const pts = []
    for (let i = 0; i < n; i++) {
      const b = (2 * Math.PI * i) / n
      const sinLat = Math.sin(lat0)*Math.cos(d) + Math.cos(lat0)*Math.sin(d)*Math.cos(b)
      const latR = Math.asin(Math.max(-1, Math.min(1, sinLat)))
      const lonR = lon0 + Math.atan2(Math.sin(b)*Math.sin(d)*Math.cos(lat0), Math.cos(d)-Math.sin(lat0)*sinLat)
      const pLat = latR * 180 / Math.PI
      let pLon = lonR * 180 / Math.PI
      while (pLon > 180) pLon -= 360
      while (pLon < -180) pLon += 360
      pts.push([pLat, pLon])
    }
    return pts
  }

  function drawFootprintSVG(lat, lon, radiusKm) {
    if (footprintSvgLayer) { map.removeLayer(footprintSvgLayer); footprintSvgLayer = null }
    if (!radiusKm || radiusKm <= 0) return

    const pts = geoCircleRaw(lat, lon, radiusKm)
    const layers = []

    // Split into segments on antimeridian jumps or polar clipping
    const segments = []
    let seg = []
    for (let i = 0; i < pts.length; i++) {
      const [pLat, pLon] = pts[i]
      // Skip points beyond Mercator limits
      if (Math.abs(pLat) > 85) {
        if (seg.length > 1) segments.push(seg)
        seg = []
        continue
      }
      if (seg.length > 0) {
        const prev = seg[seg.length - 1]
        // Antimeridian jump: lon difference > 180°
        if (Math.abs(pLon - prev[1]) > 180) {
          if (seg.length > 1) segments.push(seg)
          seg = []
        }
      }
      seg.push([pLat, pLon])
    }
    if (seg.length > 1) segments.push(seg)

    // Draw each segment as a polyline
    const polylines = segments.map(s =>
      L.polyline(s, {
        color: '#1a5a9a', weight: 1.5,
        dashArray: '7,6', opacity: 0.75,
        smoothFactor: 0,
      })
    )

    // Fill only if single segment (no antimeridian crossing)
    if (segments.length === 1 && segments[0].length > 2) {
      layers.push(L.polygon(segments[0], {
        color: 'transparent', fillColor: '#1a5a9a',
        fillOpacity: 0.07, weight: 0,
      }))
    }

    footprintSvgLayer = L.layerGroup([...layers, ...polylines]).addTo(map)
  }

  function drawGroundtrack(track) {
    groundtrackLayer?.remove()
    const segs = []; let seg = []
    track.forEach((pt, i) => {
      if (i > 0 && Math.abs(pt.lon - track[i-1].lon) > 180) { if (seg.length > 1) segs.push([...seg]); seg = [] }
      seg.push([pt.lat, pt.lon])
    })
    if (seg.length > 1) segs.push(seg)
    groundtrackLayer = L.layerGroup(segs.map(pts => L.polyline(pts, { color:'#1a5a9a', weight:2, opacity:.75, dashArray:'8,5' }))).addTo(map)
  }

  function openWatchlistPanel() { wlSelected = [...$watchlist]; showWatchlistPanel = true }
  function addToSelected(n) { if (!wlSelected.includes(n)) wlSelected = [...wlSelected, n] }
  function removeFromSelected(n) { wlSelected = wlSelected.filter(x => x !== n) }
  async function applyWatchlist() { watchlist.set([...wlSelected]); await Go.SetWatchlist(wlSelected); showWatchlistPanel = false }
  $: filteredAvail = $satList.filter(n => !wlSelected.includes(n) && (!wlSearch || n.toLowerCase().includes(wlSearch.toLowerCase()))).sort()
</script>

<div class="mc" on:mousemove={onDrag} on:mouseup={stopDrag} on:mouseleave={stopDrag}>
  <div bind:this={mapEl} class="map"></div>

  {#if popupData}
    <div class="popup"
      style={popupX !== null ? `left:${popupX}px;top:${popupY}px;transform:none` : ''}
      on:mousedown={startDrag}>
      <div class="ph"><span class="pname">{popupData.name}</span><span class="drag-hint">⠿</span><button class="px" on:click={() => popupData=null}>×</button></div>
      {#if passInfo}
        <div class="pass-row {passInfo.active ? 'pass-live' : 'pass-next'}">
          {#if passInfo.active}
            <span class="pass-dot"></span>
            <span class="pass-label">PASS IN PROGRESS</span>
            <span class="pass-maxel">Max {passInfo.maxEl?.toFixed(0)}°</span>
            <span class="pass-time">LOS {fmtPassTime(passInfo.los)} · {countdownLOS}</span>
          {:else}
            <span class="pass-label">NEXT PASS</span>
            <span class="pass-maxel">Max {passInfo.maxEl?.toFixed(0)}°</span>
            <span class="pass-time">AOS {fmtPassTime(passInfo.aos)} · {countdownStr}</span>
          {/if}
        </div>
      {/if}
      <div class="pg">
        <span class="pk">Azimuth</span>      <span class="pv">{popupData.az?.toFixed(1)}°</span>
        <span class="pk">Elevation</span>    <span class="pv {popupData.el>=0?'up':''}">{popupData.el?.toFixed(1)}°</span>
        <span class="pk">Range</span>        <span class="pv">{formatRange(popupData.range)}</span>
        <span class="pk">Altitude</span>     <span class="pv">{popupData.alt?.toFixed(0)} km</span>
        <span class="pk">Radial</span>       <span class="pv {popupData.rangeRate<0?'app':'rec'}">{popupData.rangeRate<0?'▼ Approach':'▲ Recede'} {popupData.rangeRate ? Math.abs(Math.round(popupData.rangeRate*3600)).toLocaleString('en-US') : '—'} km/h</span>
        <span class="pk">Footprint (d)</span><span class="pv">{formatRange(popupData.footprint * 2)}</span>
      </div>
    </div>
  {/if}

  <button class="wl-btn" on:click={openWatchlistPanel}>🛰 Satellites ({$watchlist.length})</button>

  {#if showWatchlistPanel}
    <div class="wl-panel">
      <div class="wl-hdr"><span>Sélection des satellites</span><button class="btn-apply" on:click={applyWatchlist}>✓ Appliquer</button><button class="px" on:click={() => showWatchlistPanel=false}>×</button></div>
      <div class="wl-body">
        <div class="wl-col">
          <div class="wl-col-hdr">Disponibles <input bind:value={wlSearch} placeholder="Filtrer…" class="wl-search"/></div>
          <div class="wl-list">{#each filteredAvail.slice(0,200) as name}<button class="wl-item" on:click={() => addToSelected(name)}><span class="wl-add">+</span>{name}</button>{/each}</div>
        </div>
        <div class="wl-sep">→</div>
        <div class="wl-col wl-col-r">
          <div class="wl-col-hdr">Sélectionnés ({wlSelected.length}) <button class="btn-clear" on:click={() => wlSelected=[]}>Effacer</button></div>
          <div class="wl-list">
            {#each [...wlSelected].sort() as name}<button class="wl-item wl-item-sel" on:click={() => removeFromSelected(name)}><span class="wl-rem">×</span>{name}</button>{/each}
            {#if !wlSelected.length}<div class="wl-empty">Aucun satellite</div>{/if}
          </div>
        </div>
      </div>
    </div>
  {/if}
</div>

<style>
  .mc  { position:relative; width:100%; height:100%; background:#a8c8e8; overflow:hidden; }
  .map { width:100%; height:100%; position:absolute; top:0; left:0; right:0; bottom:0; }

  :global(.sw) { display:flex; align-items:center; gap:5px; pointer-events:none; }
  :global(.sd) { border-radius:50%; cursor:pointer; pointer-events:all; flex-shrink:0; transition:transform .15s; }
  :global(.sw-sat) { display:flex; flex-direction:column; align-items:center; gap:3px; filter:drop-shadow(0 0 6px rgba(64,200,240,0.7)); animation:sat-pulse 2s ease-in-out infinite; }
  :global(.sw-sat svg) { cursor:pointer; }
  @keyframes sat-pulse { 0%,100%{filter:drop-shadow(0 0 5px rgba(64,200,240,0.6))} 50%{filter:drop-shadow(0 0 10px rgba(64,200,240,1))} }
  :global(.sd:hover) { transform:scale(2); }
  :global(.slabel) {
    font-family:'Inter',sans-serif; font-size:11px; font-weight:700;
    color:white; background:#1a5a9a;
    padding:2px 7px; border-radius:4px;
    white-space:nowrap; pointer-events:none;
    box-shadow:0 1px 3px rgba(0,0,0,0.3);
    margin-left:4px;
  }

  :global(.leaflet-control-zoom a) { background:#1e2e42 !important; color:#40c8f0 !important; border-color:#3a5a7a !important; }
  :global(.leaflet-control-attribution) { font-size:9px !important; background:rgba(255,255,255,0.7) !important; color:#666 !important; }

  .popup { position:absolute; top:50px; left:50%; transform:translateX(-50%); z-index:800; cursor:default; user-select:none; background:white; border:1px solid #c0d4e8; border-radius:10px; min-width:280px; box-shadow:0 8px 32px rgba(0,0,0,.6); font-family:'Inter',sans-serif; }
  .ph  { display:flex; justify-content:space-between; align-items:center; padding:10px 14px; cursor:grab; background:#f0f6ff; border-radius:10px 10px 0 0; border-bottom:1px solid #d0e0f0; }
  .pname { font-size:14px; font-weight:bold; color:#1a5a9a; flex:1; }
  .drag-hint { color:#c0ccd8; font-size:14px; cursor:grab; margin:0 6px; }
  .px { background:none; border:none; color:#9ab0c8; font-size:18px; cursor:pointer; padding:0; }
  .px:hover { color:#1a2a3a; }
  .pg { display:grid; grid-template-columns:1fr 1fr; gap:6px 12px; padding:12px 14px; font-family:"Inter",sans-serif; }
  .pk { font-size:10px; color:#9ab0c8; text-transform:uppercase; letter-spacing:.06em; align-self:center; font-weight:600; }
  .pv { font-size:13px; color:#1a2a3a; font-weight:700; }
  .pv.up { color:#1a9a50; } .pv.app { color:#1a9a50; } .pv.rec { color:#1a5a9a; }

  .pass-row {
    display:flex; align-items:center; gap:8px; flex-wrap:wrap;
    padding:7px 14px; font-size:11px; font-weight:600;
    border-bottom:1px solid #e0eaf4;
  }
  .pass-live { background:#f0f9f4; color:#1a7a40; }
  .pass-next { background:#f0f6ff; color:#1a5a9a; }
  .pass-dot  { width:7px; height:7px; border-radius:50%; background:#1a9a50; box-shadow:0 0 5px #1a9a50; animation:blink 1.2s infinite; flex-shrink:0; }
  .pass-label { font-weight:700; letter-spacing:.04em; font-size:10px; text-transform:uppercase; }
  .pass-maxel { background:rgba(0,0,0,0.06); padding:1px 6px; border-radius:10px; font-size:10px; }
  .pass-time  { color:inherit; opacity:.8; font-weight:500; margin-left:auto; font-size:10px; }
  @keyframes blink { 0%,100%{opacity:1} 50%{opacity:.3} }

  .wl-btn { position:absolute; top:10px; left:10px; z-index:700; background:rgba(26,45,68,.85); border:1px solid #4a7aaa; color:#40c8f0; font-family:'Inter',sans-serif; font-size:12px; padding:7px 14px; border-radius:6px; cursor:pointer; backdrop-filter:blur(4px); }
  .wl-btn:hover { background:rgba(36,62,90,.9); }

  .wl-panel { position:absolute; top:46px; left:10px; z-index:700; background:#1a2d44; border:1px solid #4a7aaa; border-radius:10px; width:580px; height:420px; display:flex; flex-direction:column; box-shadow:0 8px 32px rgba(0,0,0,.7); font-family:'Inter',sans-serif; }
  .wl-hdr { display:flex; align-items:center; gap:8px; padding:10px 14px; border-bottom:1px solid #2a4a6a; background:#162336; border-radius:10px 10px 0 0; font-size:11px; color:#7aaac8; flex-shrink:0; }
  .wl-hdr span { flex:1; }
  .btn-apply { background:#1a5a9a; border:none; color:#c8e8ff; font-family:'Inter',sans-serif; font-size:11px; padding:5px 12px; border-radius:4px; cursor:pointer; font-weight:bold; }
  .btn-clear { background:none; border:1px solid #3a5a7a; color:#5a8ab0; font-family:'Inter',sans-serif; font-size:10px; padding:3px 8px; border-radius:3px; cursor:pointer; }
  .wl-body { display:flex; flex:1; overflow:hidden; }
  .wl-col { display:flex; flex-direction:column; flex:1; overflow:hidden; }
  .wl-col-r { border-left:1px solid #2a4a6a; }
  .wl-col-hdr { display:flex; align-items:center; gap:8px; padding:7px 10px; background:#162336; border-bottom:1px solid #2a4a6a; font-size:10px; color:#5a8ab0; flex-shrink:0; }
  .wl-search { flex:1; background:#0e1824; border:1px solid #2a4a6a; color:#c0daf0; padding:3px 8px; font-family:'Inter',sans-serif; font-size:10px; border-radius:4px; outline:none; }
  .wl-sep { display:flex; align-items:center; justify-content:center; width:30px; flex-shrink:0; color:#3a6a8a; font-size:12px; }
  .wl-list { overflow-y:auto; flex:1; }
  .wl-list::-webkit-scrollbar { width:3px; }
  .wl-list::-webkit-scrollbar-thumb { background:#3a5a7a; }
  .wl-item { display:flex; align-items:center; gap:6px; width:100%; background:none; border:none; border-bottom:1px solid #1a2a3a; color:#7aaac8; font-family:'Inter',sans-serif; font-size:10px; padding:6px 10px; text-align:left; cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .wl-item:hover { background:#243444; color:#c0daf0; }
  .wl-item-sel { color:#40c8f0; }
  .wl-item-sel:hover { color:#ff7070; background:#1a3040; }
  .wl-add { color:#40e888; font-size:12px; font-weight:bold; flex-shrink:0; }
  .wl-rem { color:#f07060; font-size:12px; font-weight:bold; flex-shrink:0; }
  .wl-empty { padding:20px; text-align:center; color:#3a6a8a; font-size:10px; }
</style>