<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { store } from '../store'
import { useRouter } from 'vue-router'
import { SEVERITY_COLORS } from '../main'

import L from 'leaflet'
import 'leaflet.markercluster'
import 'leaflet.markercluster/dist/MarkerCluster.css'
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'

const router = useRouter()
const props = defineProps<{
    center?: [number, number],
    zoom?: number,
    site_id?: number,
    alarms?: Record<string, any>[],
    // TODO: Idk if passing map size as props is the best idea. Maybe there's a better way?
    map_height?: string,
    map_width?: string,
    robots?: Record<string, any>[]
}>()

const center = props.center ?? [39.8283, -98.5795]
const zoom = props.zoom ?? 4

const show_alarm_layer = ref(true)
const show_robot_layer = ref(true)

const DEFAULT_COLOR = 'rgba(255, 255, 255, 1)'

const get_alarms_for_site = (site_id: number) =>
    show_alarm_layer.value === false ? [] : (props.alarms ?? store.alarms).filter(a => a.site_id === site_id)

const get_marker_color = (site_id: number): string => {
    const alarms = get_alarms_for_site(site_id)
    if (alarms.length === 0) return DEFAULT_COLOR
    const worst = Math.min(...alarms.map(a => a.severity))
    return SEVERITY_COLORS[worst] ?? DEFAULT_COLOR
}

const get_site_worst_severity = (site_id: number): number | null => {
    const alarms = get_alarms_for_site(site_id)
    if (alarms.length === 0) return null
    return Math.min(...alarms.map(a => a.severity))
}

const create_icon = (color: string) => L.divIcon({
    className: '',
    html: `<div class="map_marker" style="--marker-color: ${color}"><span></span></div>`,
    iconSize: [28, 40],
    iconAnchor: [14, 40],
    popupAnchor: [0, -42]
})

const create_robot_icon = (on_call: boolean) => L.divIcon({
    className: '',
    html: `<div class="robot_marker ${on_call ? 'on_call' : 'idle'}"></div>`,
    iconSize: [14, 14],
    iconAnchor: [7, 7],
    popupAnchor: [0, -10]
})

onMounted(() => {
  const map = L.map('map').setView(center, zoom)
  L.tileLayer(
    'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
    {
      // attribution: '&copy; OpenStreetMap &copy; CARTO', // Ugly
      subdomains: 'abcd',
      maxZoom: 19
    }
  ).addTo(map)

  const markersLayer = L.markerClusterGroup({
      polygonOptions: {
          fillColor: 'rgba(255,255,255,0.1)',
          color: 'rgba(255,255,255,0.8)',
          weight: 2,
          opacity: 0.8,
          fillOpacity: 0.2
      },
      iconCreateFunction: (cluster) => {
          const severities = cluster.getAllChildMarkers()
              .map(m => (m.options as any).severity)
              .filter(s => s !== null)
          const worst = severities.length > 0 ? Math.min(...severities) : null
          const color = worst !== null ? SEVERITY_COLORS[worst] : 'rgba(255,255,255,0.8)'
          return L.divIcon({
              html: `<div class="cluster_icon" style="--cluster-color: ${color}">
                        <span>${cluster.getChildCount()}</span>
                    </div>`,
              className: '',
              iconSize: [40, 40]
          })
      }
  }).addTo(map)

  const robotsLayer = L.layerGroup().addTo(map)

  const update_robot_markers = () => {
    robotsLayer.clearLayers()
    if (!props.robots || !show_robot_layer.value) return
    props.robots.forEach(robot => {
        if (robot.lat == null || robot.lon == null) return
        L.marker([robot.lat, robot.lon], {
            icon: create_robot_icon(!!robot.current_incident_id)
        })
        .addTo(robotsLayer)
        // TODO: Maybe make the entire popup clickable instead of the text itself? Looks odd like this.
        .bindPopup(`
            <a href="#" data-id="${robot.id}" class="robot-link">${robot.name.toUpperCase()}</a><br/>
            ${robot.current_incident_id ? `INC #${robot.current_incident_id}` : 'IDLE'}<br/>
            ${robot.current_site_id ? `SITE ${robot.current_site_id}` : ''}
        `)
    })
  }

  const update_markers = () => {
    markersLayer.clearLayers()
    const bounds = map.getBounds()
    const sites = props.site_id != null
      ? store.cellsites.filter(s => s.id === props.site_id)
      : store.cellsites

      sites.forEach(site => {
      if (site.lat != null && site.lon != null) {
          const latLng = L.latLng(site.lat, site.lon)
          if (bounds.contains(latLng)) {
              const marker = L.marker([site.lat, site.lon], {
                  icon: create_icon(get_marker_color(site.id)),
                  severity: get_site_worst_severity(site.id)
              })
              marker.addTo(markersLayer)
                  .bindPopup(`<a href="#" data-id="${site.id}" class="marker-link">Site ${site.id}</a>`)
              }
          }
      })
    }


  map.on('popupopen', (e) => {
      const el = e.popup.getElement()

      const site_link = el.querySelector('.marker-link')
      site_link?.addEventListener('click', (event) => {
          event.preventDefault()
          router.push(`/cellsites/${site_link.dataset.id}`)
      }, { once: true }) // NOTE: To avoid accumulating listeners

      const robot_link = el.querySelector('.robot-link')
      robot_link?.addEventListener('click', (event) => {
          event.preventDefault()
          router.push(`/robots/${robot_link.dataset.id}`)
      }, { once: true }) // NOTE: To avoid accumulating listeners
  })


  map.whenReady(() => {
    update_markers()
    update_robot_markers()
    map.on('moveend', update_markers)
    map.on('moveend', update_robot_markers)
  })

  watch(() => store.cellsites, () => update_markers(), { deep: true })
  watch(() => props.alarms, () => update_markers(), { deep: true })
  watch(show_alarm_layer, () => update_markers())

  watch(() => props.robots, () => update_robot_markers(), { deep: true })
  watch(show_robot_layer, () => update_robot_markers())

  watch(() => props.center, (newCenter) => {
      if (newCenter) map.setView(newCenter, map.getZoom())
  })

})
</script>

<template>
  <div style="position: relative;">
    <div class="map_wrapper">
      <div class="layer_panel">
        <div class="layer_title">LAYERS</div>
        <label class="layer_item">
          
          <input type="checkbox" v-model="show_alarm_layer" />
          <span>Alarms</span>
        </label>
        <label class="layer_item">
        <input type="checkbox" v-model="show_robot_layer" />
        <span>Robots</span>
      </label>
      </div>
      <div id="map" :style="{ height: props.map_height ?? '500px', width: props.map_width ?? '100%' }"></div>
    </div>
  </div>
</template>


<!-- NOTE: style must be global for the leaflet map to pick it up (reminder: vue adds data attribute to the scoped styles) -->
<style >
.leaflet-container {
  font-family: 'Departure Mono', monospace !important;
}

.marker-cluster-small,
.marker-cluster-medium,
.marker-cluster-large {
  background-color: rgba(255,255,255,0.2)
}

.marker-cluster-small div,
.marker-cluster-medium div,
.marker-cluster-large div {
  background-color: rgba(255,255,255,0.8);
  color: #000;
}

.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
  background-color: rgba(20, 20, 20, 1) !important;
  color: var(--theme, rgba(255, 100, 0, 1)) !important;
  border: 1px solid var(--theme-faint, rgba(255, 100, 0, 0.4)) !important;
  font-family: 'Departure Mono', monospace !important;
  transition: all 0.2s;
}

.leaflet-control-zoom-in:hover,
.leaflet-control-zoom-out:hover {
  background-color: var(--theme-bg, rgba(255, 100, 0, 0.15)) !important;
  color: var(--theme, rgba(255, 100, 0, 1)) !important;
  border-color: var(--theme-dim, rgba(255, 100, 0, 0.8)) !important;
}

.leaflet-control-zoom {
  border: 1px solid var(--theme-faint, rgba(255, 100, 0, 0.4)) !important;
  border-radius: 4px !important;
  overflow: hidden;
}

.layer_panel {
    position: absolute;
    top: 1rem;
    right: 1rem;
    z-index: 1000;
    background: rgba(20, 20, 20, 1);
    border: 1px solid var(--theme-faint, rgba(255, 100, 0, 0.4));
    border-radius: 6px;
    padding: 0.5rem 0.75rem;
    font-size: 0.75rem;
    backdrop-filter: blur(4px);
}

.layer_title {
    color: var(--theme-dim, rgba(255, 100, 0, 0.6));
    margin-bottom: 0.4rem;
    letter-spacing: 0.05em;
}

.layer_item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--theme, rgba(255, 100, 0, 1));
    cursor: pointer;
}

.layer_item input[type="checkbox"] {
    accent-color: var(--theme, rgba(255, 100, 0, 1));
}

.map_marker {
  position: relative;
  width: 28px;
  height: 40px;
}

/* tower body */
.map_marker::before {
  content: '';
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 3px;
  height: 28px;
  background-color: var(--marker-color);
}

/* signal arc outer */
.map_marker::after {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 24px;
  height: 16px;
  border-radius: 24px 24px 0 0;
  border: 3px solid var(--marker-color);
  border-bottom: none;
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
}

.map_marker span {
  position: absolute;
  top: 6px;
  left: 50%;
  transform: translateX(-50%);
  width: 12px;
  height: 8px;
  border-radius: 12px 12px 0 0;
  border: 3px solid rgba(255, 255, 255, 0.6);
  border-bottom: none;
}

.map_wrapper {
    position: relative;
}

.map_wrapper::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 400;
    background: transparent;
    transition: background 0.4s;
}

.robot_marker {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.9);
    background: rgba(40, 40, 40, 0.9);
    box-shadow: 0 0 6px rgba(255, 255, 255, 0.4);
}

.robot_marker.on_call {
    border-color: var(--theme, rgba(255, 100, 0, 1));
    background: rgba(255, 100, 0, 0.2);
    box-shadow: 0 0 8px rgba(255, 100, 0, 0.6);
}


.mode_historical .map_wrapper::after {
    background: rgba(200, 160, 100, 0.06);
}

.mode_frozen .map_wrapper::after {
    background: rgba(150, 220, 255, 0.06);
}


.leaflet-popup-content-wrapper {
  background-color: #2a2a2a;
  color: rgba(255, 100, 0, 1);
  border: 1px solid rgba(255, 100, 0, 0.4);
  box-shadow: 0 0 10px rgba(255, 100, 0, 0.2);
}

.leaflet-popup-tip {
  background-color: #2a2a2a;
}

.leaflet-popup-content a {
  color: rgba(255, 100, 0, 1);
}
.leaflet-control-attribution {
  display: none;
}

.cluster_icon {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background-color: color-mix(in srgb, var(--cluster-color) 30%, transparent);
    border: 2px solid var(--cluster-color);
    display: flex;
    align-items: center;
    justify-content: center;
}
.cluster_icon span {
    color: #000;
    /* NOTE: Need the font here */
    font-family: 'Departure Mono', monospace;
    font-size: 0.75rem;
    font-weight: bold;
}
</style>