import { useEffect, useMemo } from 'react'
import { MapContainer, TileLayer, CircleMarker, Polyline, Popup, Tooltip, useMap } from 'react-leaflet'
import type { LatLngBoundsExpression, LatLngTuple } from 'leaflet'
import 'leaflet/dist/leaflet.css'
import type { NodeInfo, EdgeInfo } from '@/lib/api'
import { ExternalLink, MapPin } from 'lucide-react'

// Fix Leaflet default marker icon issue with Vite
import L from 'leaflet'
import markerIcon from 'leaflet/dist/images/marker-icon.png'
import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png'
import markerShadow from 'leaflet/dist/images/marker-shadow.png'

// @ts-expect-error - Leaflet icon fix
delete L.Icon.Default.prototype._getIconUrl
L.Icon.Default.mergeOptions({
  iconUrl: markerIcon,
  iconRetinaUrl: markerIcon2x,
  shadowUrl: markerShadow,
})

interface GeoMapProps {
  nodes: NodeInfo[]
  edges: EdgeInfo[]
  selectedNodeId: number | null
  onSelectNode: (nodeId: number | null) => void
}

const REGION_COLORS = ['#3b82f6', '#a78bfa', '#06b6d4', '#f59e0b', '#22c55e', '#ec4899', '#8b5cf6', '#14b8a6']
const INFRA_ROLES = ['ROUTER', 'ROUTER_LATE', 'REPEATER', 'TRACKER']

function getQualityColor(snr: number): string {
  if (snr > 12) return '#22c55e'
  if (snr > 8) return '#4ade80'
  if (snr > 5) return '#f59e0b'
  if (snr > 3) return '#f97316'
  return '#ef4444'
}

function getRegionIndex(lat: number | null): number {
  if (lat === null) return 0
  if (lat > 46) return 0
  if (lat > 44.5) return 1
  if (lat > 43) return 2
  return 3
}

function formatLastHeard(lastHeard: string | null): string {
  if (!lastHeard) return 'Unknown'
  const date = new Date(lastHeard)
  const now = new Date()
  const diffMs = now.getTime() - date.getTime()
  const diffMins = Math.floor(diffMs / 60000)
  const diffHours = Math.floor(diffMs / 3600000)
  const diffDays = Math.floor(diffMs / 86400000)

  if (diffMins < 1) return 'Just now'
  if (diffMins < 60) return `${diffMins}m ago`
  if (diffHours < 24) return `${diffHours}h ago`
  return `${diffDays}d ago`
}

// Component to fit bounds on mount
function FitBounds({ bounds }: { bounds: LatLngBoundsExpression | null }) {
  const map = useMap()

  useEffect(() => {
    if (bounds) {
      map.fitBounds(bounds, { padding: [50, 50] })
    }
  }, [map, bounds])

  return null
}

interface NodePopupProps {
  node: NodeInfo
}

function NodePopup({ node }: NodePopupProps) {
  const hasCoords = node.latitude !== null && node.longitude !== null
  const batteryText = node.battery_level !== null
    ? (node.battery_level > 100 || (node.voltage && node.voltage > 4.1) ? 'USB ⚡' : `${node.battery_level.toFixed(0)}%`)
    : 'Unknown'

  return (
    <div className="min-w-[200px]">
      <div className="font-semibold text-slate-800">{node.short_name}</div>
      <div className="text-xs text-slate-600 mb-2">{node.long_name}</div>

      <div className="grid grid-cols-2 gap-x-4 gap-y-1 text-xs">
        <div className="text-slate-500">Role</div>
        <div className="text-slate-700 font-medium">{node.role}</div>

        <div className="text-slate-500">Hardware</div>
        <div className="text-slate-700">{node.hardware || 'Unknown'}</div>

        <div className="text-slate-500">Battery</div>
        <div className="text-slate-700">{batteryText}</div>

        <div className="text-slate-500">Last Heard</div>
        <div className="text-slate-700">{formatLastHeard(node.last_heard)}</div>
      </div>

      {hasCoords && (
        <div className="mt-3 pt-2 border-t border-slate-200 flex gap-2">
          <a
            href={`https://www.google.com/maps?q=${node.latitude},${node.longitude}`}
            target="_blank"
            rel="noopener noreferrer"
            className="flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800"
          >
            <ExternalLink size={10} />
            Google Maps
          </a>
          <a
            href={`https://www.openstreetmap.org/?mlat=${node.latitude}&mlon=${node.longitude}&zoom=14`}
            target="_blank"
            rel="noopener noreferrer"
            className="flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800"
          >
            <ExternalLink size={10} />
            OSM
          </a>
        </div>
      )}
    </div>
  )
}

export default function GeoMap({
  nodes,
  edges,
  selectedNodeId,
  onSelectNode,
}: GeoMapProps) {
  // Filter nodes with valid coordinates
  const geoNodes = useMemo(() =>
    nodes.filter((n) => n.latitude !== null && n.longitude !== null),
    [nodes]
  )

  const nodesWithoutCoords = nodes.length - geoNodes.length

  // Create node map for edge lookup
  const nodeMap = useMemo(() =>
    new Map(geoNodes.map((n) => [n.node_num, n])),
    [geoNodes]
  )

  // Filter edges where both nodes have coordinates
  const geoEdges = useMemo(() =>
    edges.filter((e) => nodeMap.has(e.from_node) && nodeMap.has(e.to_node)),
    [edges, nodeMap]
  )

  // Calculate bounds
  const bounds = useMemo((): LatLngBoundsExpression | null => {
    if (geoNodes.length === 0) return null
    const lats = geoNodes.map((n) => n.latitude!)
    const lons = geoNodes.map((n) => n.longitude!)
    return [
      [Math.min(...lats), Math.min(...lons)],
      [Math.max(...lats), Math.max(...lons)],
    ]
  }, [geoNodes])

  // Default center (Idaho)
  const defaultCenter: LatLngTuple = [43.6, -114.4]

  // Get neighbors of selected node
  const selectedNeighbors = useMemo(() => {
    const neighbors = new Set<number>()
    if (selectedNodeId !== null) {
      edges.forEach((e) => {
        if (e.from_node === selectedNodeId) neighbors.add(e.to_node)
        if (e.to_node === selectedNodeId) neighbors.add(e.from_node)
      })
    }
    return neighbors
  }, [selectedNodeId, edges])

  return (
    <div className="relative bg-bg-card border border-border overflow-hidden">
      <MapContainer
        center={defaultCenter}
        zoom={7}
        style={{ width: '100%', height: '540px' }}
        className="z-0"
      >
        <TileLayer
          url="https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"
          attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>, &copy; <a href="https://carto.com/attributions">CARTO</a>'
        />

        <FitBounds bounds={bounds} />

        {/* Edges */}
        {geoEdges.map((edge, i) => {
          const fromNode = nodeMap.get(edge.from_node)!
          const toNode = nodeMap.get(edge.to_node)!
          const isRelated = selectedNodeId === null ||
            edge.from_node === selectedNodeId ||
            edge.to_node === selectedNodeId

          return (
            <Polyline
              key={i}
              positions={[
                [fromNode.latitude!, fromNode.longitude!],
                [toNode.latitude!, toNode.longitude!],
              ]}
              color={getQualityColor(edge.snr)}
              weight={isRelated && selectedNodeId !== null ? 2.5 : 1.5}
              opacity={selectedNodeId === null ? 0.3 : (isRelated ? 0.6 : 0.08)}
            />
          )
        })}

        {/* Nodes */}
        {geoNodes.map((node) => {
          const isSelected = node.node_num === selectedNodeId
          const isNeighbor = selectedNeighbors.has(node.node_num)
          const isRelated = selectedNodeId === null || isSelected || isNeighbor
          const isInfra = INFRA_ROLES.includes(node.role)
          const regionIndex = getRegionIndex(node.latitude)
          const color = REGION_COLORS[regionIndex % REGION_COLORS.length]

          return (
            <CircleMarker
              key={node.node_num}
              center={[node.latitude!, node.longitude!]}
              radius={isInfra ? 8 : 5}
              fillColor={isInfra ? color : '#111827'}
              fillOpacity={isRelated ? 0.9 : 0.2}
              stroke={true}
              color={isSelected ? '#ffffff' : color}
              weight={isSelected ? 3 : isInfra ? 0 : 2}
              opacity={isRelated ? 1 : 0.3}
              eventHandlers={{
                click: () => onSelectNode(isSelected ? null : node.node_num),
              }}
            >
              <Tooltip direction="top" offset={[0, -8]}>
                <span className="font-mono text-xs">{node.short_name}</span>
              </Tooltip>
              <Popup>
                <NodePopup node={node} />
              </Popup>
            </CircleMarker>
          )
        })}
      </MapContainer>

      {/* Stats overlay */}
      <div className="absolute bottom-4 left-4 bg-bg-card/90 backdrop-blur-sm border border-border rounded px-3 py-2 text-xs text-slate-400 flex items-center gap-2">
        <MapPin size={12} />
        <span>
          Showing {geoNodes.length} of {nodes.length} nodes
          {nodesWithoutCoords > 0 && (
            <span className="text-slate-500"> ({nodesWithoutCoords} without coordinates)</span>
          )}
        </span>
      </div>
    </div>
  )
}
