import { describe, it, expect, beforeEach, vi } from 'vitest'
import { createMapStore } from './store'
import { CartoBasemaps } from '../../const'
import type { Deck, PickingInfo } from '@deck.gl/core'

// Mock WebMercatorViewport
vi.mock('@deck.gl/core', async () => {
  const actual = await vi.importActual('@deck.gl/core')
  return {
    ...actual,
    WebMercatorViewport: class MockWebMercatorViewport {
      props: Record<string, unknown>
      constructor(props: Record<string, unknown>) {
        this.props = props
        Object.assign(this, props)
      }
      fitBounds(bounds: number[][]) {
        return {
          latitude: ((bounds[0]?.[0] ?? 0) + (bounds[1]?.[0] ?? 0)) / 2,
          longitude: ((bounds[0]?.[1] ?? 0) + (bounds[1]?.[1] ?? 0)) / 2,
          zoom: 10,
        }
      }
      project(coords: [number, number]) {
        return [coords[0] * 100, coords[1] * 100]
      }
    },
  }
})

describe('MapStore', () => {
  const mapId = 'test-map'
  let store: ReturnType<typeof createMapStore>

  beforeEach(() => {
    store = createMapStore([
      {
        id: mapId,
        basemap: CartoBasemaps.positron,
        initialViewState: {
          longitude: 0,
          latitude: 0,
          zoom: 1,
        },
      },
    ])
  })

  describe('initialization', () => {
    it('should initialize with default props', () => {
      const state = store.getState()
      expect(state.maps[mapId]).toBeDefined()
      expect(state.maps[mapId]?.basemap).toBe(CartoBasemaps.positron)
      expect(state.maps[mapId]?.isLoaded).toBe(false)
      expect(state.maps[mapId]?.controller).toBe(true)
      expect(state.maps[mapId]?.layers).toEqual([])
    })

    it('should initialize with custom layers', () => {
      const customStore = createMapStore([
        {
          id: 'custom-map',
          basemap: CartoBasemaps.voyager,
          layers: ['layer-1', 'layer-2'],
          initialViewState: {
            longitude: 0,
            latitude: 0,
            zoom: 1,
          },
        },
      ])

      const state = customStore.getState()
      expect(state.maps['custom-map']?.layers).toEqual(['layer-1', 'layer-2'])
    })

    it('should initialize with custom controller', () => {
      const customStore = createMapStore([
        {
          id: 'custom-map',
          basemap: CartoBasemaps.positron,
          controller: false,
          initialViewState: {
            longitude: 0,
            latitude: 0,
            zoom: 1,
          },
        },
      ])

      const state = customStore.getState()
      expect(state.maps['custom-map']?.controller).toBe(false)
    })

    it('should initialize multiple maps', () => {
      const multiStore = createMapStore([
        {
          id: 'map-1',
          basemap: CartoBasemaps.positron,
          initialViewState: {
            longitude: 0,
            latitude: 0,
            zoom: 1,
          },
        },
        {
          id: 'map-2',
          basemap: CartoBasemaps.darkMatter,
          initialViewState: {
            longitude: 0,
            latitude: 0,
            zoom: 1,
          },
        },
      ])

      const state = multiStore.getState()
      expect(state.maps['map-1']).toBeDefined()
      expect(state.maps['map-2']).toBeDefined()
      expect(state.maps['map-1']?.basemap).toBe(CartoBasemaps.positron)
      expect(state.maps['map-2']?.basemap).toBe(CartoBasemaps.darkMatter)
    })
  })

  describe('setBasemap', () => {
    it('should update basemap', () => {
      store.getState().setBasemap(mapId, CartoBasemaps.darkMatter)

      const state = store.getState()
      expect(state.maps[mapId]?.basemap).toBe(CartoBasemaps.darkMatter)
    })

    it('should preserve isLoaded when changing to same map type', () => {
      // Set map as loaded
      store.getState().setLoad(mapId, true)
      expect(store.getState().maps[mapId]?.isLoaded).toBe(true)

      // Change to different basemap of same type (both are maplibre)
      store.getState().setBasemap(mapId, CartoBasemaps.voyager)

      const state = store.getState()
      expect(state.maps[mapId]?.basemap).toBe(CartoBasemaps.voyager)
      expect(state.maps[mapId]?.isLoaded).toBe(true)
    })

    it('should reset isLoaded when changing to different map type', () => {
      // This test documents behavior - would need MAPS_BASEMAP data
      store.getState().setLoad(mapId, true)
      store.getState().setBasemap(mapId, CartoBasemaps.darkMatter)

      const state = store.getState()
      expect(state.maps[mapId]?.basemap).toBe(CartoBasemaps.darkMatter)
    })
  })

  describe('setLoad', () => {
    it('should set isLoaded to true', () => {
      store.getState().setLoad(mapId, true)

      const state = store.getState()
      expect(state.maps[mapId]?.isLoaded).toBe(true)
    })

    it('should set isLoaded to false', () => {
      store.getState().setLoad(mapId, true)
      store.getState().setLoad(mapId, false)

      const state = store.getState()
      expect(state.maps[mapId]?.isLoaded).toBe(false)
    })

    it('should toggle isLoaded', () => {
      expect(store.getState().maps[mapId]?.isLoaded).toBe(false)

      store.getState().setLoad(mapId, true)
      expect(store.getState().maps[mapId]?.isLoaded).toBe(true)

      store.getState().setLoad(mapId, false)
      expect(store.getState().maps[mapId]?.isLoaded).toBe(false)
    })
  })

  describe('addLayers', () => {
    it('should add layers to empty array', () => {
      store.getState().addLayers(mapId, ['layer-1', 'layer-2'])

      const state = store.getState()
      expect(state.maps[mapId]?.layers).toEqual(['layer-1', 'layer-2'])
    })

    it('should add layers to existing array', () => {
      store.getState().addLayers(mapId, ['layer-1'])
      store.getState().addLayers(mapId, ['layer-2', 'layer-3'])

      const state = store.getState()
      expect(state.maps[mapId]?.layers).toEqual([
        'layer-1',
        'layer-2',
        'layer-3',
      ])
    })

    it('should not add duplicate layers', () => {
      store.getState().addLayers(mapId, ['layer-1', 'layer-2'])
      store.getState().addLayers(mapId, ['layer-2', 'layer-3'])

      const state = store.getState()
      // Set should dedupe
      expect(state.maps[mapId]?.layers).toEqual([
        'layer-1',
        'layer-2',
        'layer-3',
      ])
    })

    it('should handle empty array', () => {
      store.getState().addLayers(mapId, ['layer-1'])
      store.getState().addLayers(mapId, [])

      const state = store.getState()
      expect(state.maps[mapId]?.layers).toEqual(['layer-1'])
    })
  })

  describe('removeLayers', () => {
    beforeEach(() => {
      store.getState().addLayers(mapId, ['layer-1', 'layer-2', 'layer-3'])
    })

    it('should remove single layer', () => {
      store.getState().removeLayers(mapId, ['layer-2'])

      const state = store.getState()
      expect(state.maps[mapId]?.layers).toEqual(['layer-1', 'layer-3'])
    })

    it('should remove multiple layers', () => {
      store.getState().removeLayers(mapId, ['layer-1', 'layer-3'])

      const state = store.getState()
      expect(state.maps[mapId]?.layers).toEqual(['layer-2'])
    })

    it('should handle removing non-existent layer', () => {
      store.getState().removeLayers(mapId, ['layer-99'])

      const state = store.getState()
      expect(state.maps[mapId]?.layers).toEqual([
        'layer-1',
        'layer-2',
        'layer-3',
      ])
    })

    it('should handle empty array', () => {
      store.getState().removeLayers(mapId, [])

      const state = store.getState()
      expect(state.maps[mapId]?.layers).toEqual([
        'layer-1',
        'layer-2',
        'layer-3',
      ])
    })

    it('should handle removing all layers', () => {
      store.getState().removeLayers(mapId, ['layer-1', 'layer-2', 'layer-3'])

      const state = store.getState()
      expect(state.maps[mapId]?.layers).toEqual([])
    })
  })

  describe('getController', () => {
    it('should return controller value', () => {
      const controller = store.getState().getController(mapId)
      expect(controller).toBe(true)
    })

    it('should return false when controller is disabled', () => {
      const customStore = createMapStore([
        {
          id: 'test',
          basemap: CartoBasemaps.positron,
          controller: false,
          initialViewState: {
            longitude: 10,
            latitude: 20,
            zoom: 5,
          },
        },
      ])

      const controller = customStore.getState().getController('test')
      expect(controller).toBe(false)
    })

    it('should return empty object for non-existent map', () => {
      const controller = store.getState().getController('non-existent')
      expect(controller).toEqual({})
    })
  })

  describe('setController', () => {
    it('should set controller to false', () => {
      store.getState().setController(mapId, false)

      const state = store.getState()
      expect(state.maps[mapId]?.controller).toBe(false)
    })

    it('should set controller to true', () => {
      store.getState().setController(mapId, false)
      store.getState().setController(mapId, true)

      const state = store.getState()
      expect(state.maps[mapId]?.controller).toBe(true)
    })

    it('should set controller object', () => {
      const controllerConfig = {
        scrollZoom: true,
        dragPan: true,
        dragRotate: false,
      }

      store.getState().setController(mapId, controllerConfig)

      const state = store.getState()
      expect(state.maps[mapId]?.controller).toEqual(controllerConfig)
    })

    it('should merge controller objects', () => {
      store.getState().setController(mapId, {
        scrollZoom: true,
        dragPan: true,
      })

      store.getState().setController(mapId, {
        dragRotate: false,
      })

      const state = store.getState()
      const controller = state.maps[mapId]?.controller as Record<
        string,
        unknown
      >
      expect(controller.scrollZoom).toBe(true)
      expect(controller.dragPan).toBe(true)
      expect(controller.dragRotate).toBe(false)
    })

    it('should handle setting to undefined', () => {
      store.getState().setController(mapId, { scrollZoom: true })
      store.getState().setController(mapId, undefined)

      const state = store.getState()
      expect(state.maps[mapId]?.controller).toBeUndefined()
    })
  })

  describe('areLayersLoaded', () => {
    it('should initialize as false', () => {
      const state = store.getState()
      expect(state.areLayersLoaded[mapId]).toBe(false)
    })

    it('should set layers loaded to true', () => {
      store.getState().setLayersLoad(mapId, true)

      const state = store.getState()
      expect(state.areLayersLoaded[mapId]).toBe(true)
    })

    it('should set layers loaded to false', () => {
      store.getState().setLayersLoad(mapId, true)
      store.getState().setLayersLoad(mapId, false)

      const state = store.getState()
      expect(state.areLayersLoaded[mapId]).toBe(false)
    })

    it('should handle multiple maps independently', () => {
      const multiStore = createMapStore([
        {
          id: 'map-1',
          basemap: CartoBasemaps.positron,
          initialViewState: {
            longitude: 10,
            latitude: 20,
            zoom: 5,
          },
        },
        {
          id: 'map-2',
          basemap: CartoBasemaps.darkMatter,
          initialViewState: {
            longitude: 10,
            latitude: 20,
            zoom: 5,
          },
        },
      ])

      multiStore.getState().setLayersLoad('map-1', true)
      multiStore.getState().setLayersLoad('map-2', false)

      const state = multiStore.getState()
      expect(state.areLayersLoaded['map-1']).toBe(true)
      expect(state.areLayersLoaded['map-2']).toBe(false)
    })
  })

  describe('areMapsIdle', () => {
    it('should initialize as false', () => {
      const state = store.getState()
      expect(state.areMapsIdle[mapId]).toBe(false)
    })

    it('should set idle to true via setIdle', () => {
      store.getState().setIdle(mapId, true)

      expect(store.getState().areMapsIdle[mapId]).toBe(true)
    })

    it('should reset idle to false on setViewState', () => {
      store.getState().setIdle(mapId, true)
      expect(store.getState().areMapsIdle[mapId]).toBe(true)

      store.getState().setViewState(mapId, {
        longitude: 5,
        latitude: 5,
        zoom: 3,
      })

      expect(store.getState().areMapsIdle[mapId]).toBe(false)
    })

    it('should reset idle to false on setZoom', () => {
      store.getState().setIdle(mapId, true)
      store.getState().setZoom(mapId, 7)
      expect(store.getState().areMapsIdle[mapId]).toBe(false)
    })

    it('should reset idle to false on setMinZoom', () => {
      store.getState().setIdle(mapId, true)
      store.getState().setMinZoom(mapId, 2)
      expect(store.getState().areMapsIdle[mapId]).toBe(false)
    })

    it('should reset idle to false on setMaxZoom', () => {
      store.getState().setIdle(mapId, true)
      store.getState().setMaxZoom(mapId, 18)
      expect(store.getState().areMapsIdle[mapId]).toBe(false)
    })
  })

  describe('viewStates', () => {
    it('should initialize viewStates', () => {
      const state = store.getState()
      expect(state.viewStates[mapId]).toBeDefined()
      expect(state.viewStates[mapId]?.longitude).toBe(0)
      expect(state.viewStates[mapId]?.latitude).toBe(0)
      expect(state.viewStates[mapId]?.zoom).toBe(1)
    })

    it('should initialize initialViewStates', () => {
      const state = store.getState()
      expect(state.initialViewStates[mapId]).toBeDefined()
      expect(state.initialViewStates[mapId]?.longitude).toBe(0)
    })

    it('should setViewState', () => {
      store.getState().setViewState(mapId, {
        longitude: 10,
        latitude: 20,
        zoom: 5,
      })

      const state = store.getState()
      expect(state.viewStates[mapId]?.longitude).toBe(10)
      expect(state.viewStates[mapId]?.latitude).toBe(20)
      expect(state.viewStates[mapId]?.zoom).toBe(5)
    })

    it('should merge viewState updates', () => {
      store.getState().setViewState(mapId, {
        longitude: 10,
        latitude: 20,
        zoom: 5,
      })

      store.getState().setViewState(mapId, {
        longitude: 10,
        latitude: 20,
        zoom: 8,
      })

      const state = store.getState()
      expect(state.viewStates[mapId]?.longitude).toBe(10)
      expect(state.viewStates[mapId]?.latitude).toBe(20)
      expect(state.viewStates[mapId]?.zoom).toBe(8)
    })

    it('should initialize viewports as null', () => {
      const state = store.getState()
      expect(state.viewports[mapId]).toBeNull()
    })

    it('should setZoom', () => {
      store.getState().setZoom(mapId, 10)

      const state = store.getState()
      expect(state.viewStates[mapId]?.zoom).toBe(10)
      expect(state.viewStates[mapId]?.longitude).toBe(0)
      expect(state.viewStates[mapId]?.latitude).toBe(0)
    })

    it('should setMinZoom', () => {
      store.getState().setMinZoom(mapId, 2)

      const state = store.getState()
      expect(state.viewStates[mapId]?.minZoom).toBe(2)
    })

    it('should setMaxZoom', () => {
      store.getState().setMaxZoom(mapId, 15)

      const state = store.getState()
      expect(state.viewStates[mapId]?.maxZoom).toBe(15)
    })

    it('should setInitialViewState', () => {
      const newInitialViewState = {
        longitude: 10,
        latitude: 20,
        zoom: 8,
      }

      store.getState().setInitialViewState(mapId, newInitialViewState)

      const state = store.getState()
      expect(state.initialViewStates[mapId]).toEqual(newInitialViewState)
      // Current viewState should not change
      expect(state.viewStates[mapId]?.longitude).toBe(0)
    })
  })

  describe('getOverlayInstance', () => {
    it('should return null for uninitialized overlay', () => {
      const overlay = store.getState().getOverlayInstance(mapId) as unknown
      expect(overlay).toBeNull()
    })

    it('should return undefined for non-existent map', () => {
      const overlay = store
        .getState()
        .getOverlayInstance('non-existent') as unknown
      expect(overlay).toBeUndefined()
    })
  })

  describe('flyTo', () => {
    it('should update view state with target', () => {
      const target = {
        longitude: 10,
        latitude: 20,
        zoom: 8,
      }

      store.getState().flyTo(mapId, target)

      const state = store.getState()
      expect(state.viewStates[mapId]?.longitude).toBe(10)
      expect(state.viewStates[mapId]?.latitude).toBe(20)
      expect(state.viewStates[mapId]?.zoom).toBe(8)
    })

    it('should apply transition config', () => {
      const target = {
        longitude: 10,
        latitude: 20,
        zoom: 8,
      }

      store.getState().flyTo(mapId, target)

      const state = store.getState()
      // Should have transition properties from transitionFlyConfig
      expect(state.viewStates[mapId]?.transitionDuration).toBeDefined()
    })

    it('should merge custom viewStateOptions', () => {
      const target = {
        longitude: 10,
        latitude: 20,
        zoom: 8,
      }

      store.getState().flyTo(mapId, target, {
        viewStateOptions: { transitionDuration: 500 },
      })

      const state = store.getState()
      expect(state.viewStates[mapId]?.transitionDuration).toBe(500)
    })
  })

  describe('loadInitialViewState', () => {
    it('should set and apply initial view state', () => {
      const newInitialViewState = {
        longitude: 15,
        latitude: 25,
        zoom: 10,
      }

      store.getState().loadInitialViewState(mapId, newInitialViewState)

      const state = store.getState()
      expect(state.initialViewStates[mapId]).toEqual(newInitialViewState)
      expect(state.viewStates[mapId]?.longitude).toBe(15)
      expect(state.viewStates[mapId]?.latitude).toBe(25)
      expect(state.viewStates[mapId]?.zoom).toBe(10)
    })

    it('should apply with zero transition duration', () => {
      const newInitialViewState = {
        longitude: 15,
        latitude: 25,
        zoom: 10,
      }

      store.getState().loadInitialViewState(mapId, newInitialViewState)

      const state = store.getState()
      expect(state.viewStates[mapId]?.transitionDuration).toBe(0)
    })
  })

  describe('resetViewState', () => {
    it('should reset to initial view state', () => {
      // Change view state
      store.getState().setViewState(mapId, {
        longitude: 100,
        latitude: 50,
        zoom: 15,
      })

      // Reset
      store.getState().resetViewState(mapId)

      const state = store.getState()
      // Should go back to initial values
      expect(state.viewStates[mapId]?.longitude).toBe(0)
      expect(state.viewStates[mapId]?.latitude).toBe(0)
      expect(state.viewStates[mapId]?.zoom).toBe(1)
    })

    it('should handle non-existent map', () => {
      // Should not throw
      expect(() => {
        store.getState().resetViewState('non-existent')
      }).not.toThrow()
    })
  })

  describe('spatialFilter', () => {
    it('should initialize spatial filter as empty object', () => {
      const state = store.getState()
      expect(state.spatialFilter[mapId]).toEqual({})
    })

    it('should set spatial filter', () => {
      const filter = {
        type: 'Polygon' as const,
        coordinates: [
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [0, 1],
            [0, 0],
          ],
        ],
      }

      store.getState().setSpatialFilter(mapId, 'filter-1', filter)

      const state = store.getState()
      expect(state.spatialFilter[mapId]?.['filter-1']).toEqual(filter)
    })

    it('should set multiple spatial filters', () => {
      const filter1 = {
        type: 'Polygon' as const,
        coordinates: [
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [0, 0],
          ],
        ],
      }
      const filter2 = {
        type: 'Polygon' as const,
        coordinates: [
          [
            [2, 2],
            [3, 2],
            [3, 3],
            [2, 2],
          ],
        ],
      }

      store.getState().setSpatialFilter(mapId, 'filter-1', filter1)
      store.getState().setSpatialFilter(mapId, 'filter-2', filter2)

      const state = store.getState()
      expect(state.spatialFilter[mapId]?.['filter-1']).toEqual(filter1)
      expect(state.spatialFilter[mapId]?.['filter-2']).toEqual(filter2)
    })

    it('should remove spatial filter', () => {
      const filter = {
        type: 'Polygon' as const,
        coordinates: [
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [0, 0],
          ],
        ],
      }

      store.getState().setSpatialFilter(mapId, 'filter-1', filter)
      store.getState().removeSpatialFilter(mapId, 'filter-1')

      const state = store.getState()
      expect(state.spatialFilter[mapId]?.['filter-1']).toBeUndefined()
    })

    it('should handle removing non-existent filter', () => {
      expect(() => {
        store.getState().removeSpatialFilter(mapId, 'non-existent')
      }).not.toThrow()
    })

    it('should handle removing filter from non-existent map', () => {
      expect(() => {
        store.getState().removeSpatialFilter('non-existent', 'filter-1')
      }).not.toThrow()
    })
  })

  describe('getDeckInstance', () => {
    it('should return undefined when no instance exists', () => {
      const deckInstance = store.getState().getDeckInstance(mapId)
      expect(deckInstance).toBeUndefined()
    })

    it('should return undefined for non-existent map', () => {
      const deckInstance = store.getState().getDeckInstance('non-existent')
      expect(deckInstance).toBeUndefined()
    })

    it('should access deck property from instanceRef', () => {
      // Set up a mock deck instance
      const mockDeck = { viewState: { zoom: 5 }, width: 800, height: 600 }
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: mockDeck,
      }

      const deckInstance = store.getState().getDeckInstance(mapId)
      expect(deckInstance).toBeDefined()
      expect(deckInstance).toBe(mockDeck)
    })

    it('should access _deck property from GoogleMapsOverlay', () => {
      // Set up a mock Google Maps overlay
      const mockDeck = { viewState: { zoom: 5 }, width: 800, height: 600 }
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking Google Maps overlay
        deck: mockDeck,
      }

      const deckInstance = store.getState().getDeckInstance(mapId)
      expect(deckInstance).toBeDefined()
      expect(deckInstance).toBe(mockDeck)
    })
  })

  describe('getViewState', () => {
    it('should return undefined when no deck instance', () => {
      const viewState = store.getState().getViewState(mapId)
      expect(viewState).toBeUndefined()
    })

    it('should return undefined when deck has no viewState', () => {
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {},
      }
      const viewState = store.getState().getViewState(mapId)
      expect(viewState).toBeUndefined()
    })

    it('should return viewState from deck instance', () => {
      const mockViewState = {
        longitude: 10,
        latitude: 20,
        zoom: 8,
      }
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          viewState: mockViewState,
          width: 800,
          height: 600,
        },
      }

      const viewState = store
        .getState()
        .getViewState(mapId) as Deck['viewState'] & {
        width: number
        height: number
      }
      expect(viewState).toBeDefined()
      expect(viewState?.longitude).toBe(10)
      expect(viewState?.latitude).toBe(20)
      expect(viewState?.zoom).toBe(8)
      expect(viewState?.width).toBe(800)
      expect(viewState?.height).toBe(600)
    })

    it('should use deck dimensions when viewState dimensions missing', () => {
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          viewState: { longitude: 10, latitude: 20, zoom: 8 },
          width: 1024,
          height: 768,
        },
      }

      const viewState = store
        .getState()
        .getViewState(mapId) as Deck['viewState'] & {
        width: number
        height: number
      }
      expect(viewState?.width).toBe(1024)
      expect(viewState?.height).toBe(768)
    })
  })

  describe('getViewports', () => {
    it('should return undefined when no deck instance', () => {
      const viewports = store.getState().getViewports(mapId)
      expect(viewports).toBeUndefined()
    })

    it('should call getViewports on deck instance', () => {
      const mockGetViewports = vi.fn().mockReturnValue([{ id: 'viewport1' }])
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
        },
      }

      const viewports = store.getState().getViewports(mapId)
      expect(mockGetViewports).toHaveBeenCalled()
      expect(viewports).toEqual([{ id: 'viewport1' }])
    })

    it('should pass rect parameter to getViewports', () => {
      const mockGetViewports = vi.fn().mockReturnValue([])
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
        },
      }

      const rect = { x: 0, y: 0, width: 100, height: 100 }
      store.getState().getViewports(mapId, rect)
      expect(mockGetViewports).toHaveBeenCalledWith(rect)
    })
  })

  describe('getViewport', () => {
    it('should return undefined when no deck instance', () => {
      const viewport = store.getState().getViewport(mapId)
      expect(viewport).toBeUndefined()
    })

    it('should return first viewport by default', () => {
      const mockViewports = [{ id: 'viewport1' }, { id: 'viewport2' }]
      const mockGetViewports = vi.fn().mockReturnValue(mockViewports)
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
        },
      }

      const viewport = store.getState().getViewport(mapId)
      expect(viewport).toEqual({ id: 'viewport1' })
    })

    it('should return viewport at specified index', () => {
      const mockViewports = [{ id: 'viewport1' }, { id: 'viewport2' }]
      const mockGetViewports = vi.fn().mockReturnValue(mockViewports)
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
        },
      }

      const viewport = store.getState().getViewport(mapId, 1)
      expect(viewport).toEqual({ id: 'viewport2' })
    })

    it('should pass rect parameter', () => {
      const mockGetViewports = vi.fn().mockReturnValue([{ id: 'viewport1' }])
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
        },
      }

      const rect = { x: 0, y: 0, width: 100, height: 100 }
      store.getState().getViewport(mapId, 0, rect)
      expect(mockGetViewports).toHaveBeenCalledWith(rect)
    })
  })

  describe('getBounds', () => {
    it('should return undefined when map not loaded', () => {
      const bounds: [[number, number], [number, number]] = [
        [0, 0],
        [10, 10],
      ]
      const result = store.getState().getBounds(mapId, bounds)
      expect(result).toBeUndefined()
    })

    it('should return undefined when no viewState available', () => {
      store.getState().setLoad(mapId, true)
      const bounds: [[number, number], [number, number]] = [
        [0, 0],
        [10, 10],
      ]
      const result = store.getState().getBounds(mapId, bounds)
      expect(result).toBeUndefined()
    })

    it('should calculate bounds with viewState', () => {
      // Set up loaded state and viewState
      store.getState().setLoad(mapId, true)
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          viewState: {
            longitude: 5,
            latitude: 5,
            zoom: 8,
          },
          width: 800,
          height: 600,
        },
      }

      const bounds: [[number, number], [number, number]] = [
        [0, 0],
        [10, 10],
      ]
      const result = store.getState().getBounds(mapId, bounds)

      expect(result).toBeDefined()
      expect(result?.latitude).toBeDefined()
      expect(result?.longitude).toBeDefined()
      expect(result?.zoom).toBeDefined()
    })

    it('should merge viewStateOptions', () => {
      store.getState().setLoad(mapId, true)
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          viewState: { longitude: 5, latitude: 5, zoom: 8 },
          width: 800,
          height: 600,
        },
      }

      const bounds: [[number, number], [number, number]] = [
        [0, 0],
        [10, 10],
      ]
      const result = store.getState().getBounds(mapId, bounds, {
        viewStateOptions: { transitionDuration: 500 },
      })

      expect(result?.transitionDuration).toBe(500)
    })
  })

  describe('fitBounds', () => {
    it('should not update when getBounds returns undefined', () => {
      const initialViewState = { ...store.getState().viewStates[mapId] }
      const bounds: [[number, number], [number, number]] = [
        [0, 0],
        [10, 10],
      ]

      store.getState().fitBounds(mapId, bounds)

      const finalViewState = store.getState().viewStates[mapId]
      expect(finalViewState).toEqual(initialViewState)
    })

    it('should update viewState when bounds are valid', () => {
      // Set up for successful getBounds
      store.getState().setLoad(mapId, true)
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          viewState: { longitude: 5, latitude: 5, zoom: 8 },
          width: 800,
          height: 600,
        },
      }

      const bounds: [[number, number], [number, number]] = [
        [0, 0],
        [10, 10],
      ]
      store.getState().fitBounds(mapId, bounds)

      const viewState = store.getState().viewStates[mapId]
      expect(viewState?.latitude).toBeDefined()
      expect(viewState?.longitude).toBeDefined()
      expect(viewState?.zoom).toBe(10)
    })
  })

  describe('multiple maps', () => {
    it('should manage state independently for each map', () => {
      const multiStore = createMapStore([
        {
          id: 'map-1',
          basemap: CartoBasemaps.positron,
          initialViewState: { longitude: 0, latitude: 0, zoom: 1 },
        },
        {
          id: 'map-2',
          basemap: CartoBasemaps.darkMatter,
          initialViewState: { longitude: 0, latitude: 0, zoom: 1 },
        },
      ])

      multiStore.getState().setLoad('map-1', true)
      multiStore.getState().addLayers('map-1', ['layer-1'])
      multiStore.getState().setBasemap('map-2', CartoBasemaps.voyager)
      multiStore.getState().addLayers('map-2', ['layer-2'])

      const state = multiStore.getState()
      expect(state.maps['map-1']?.isLoaded).toBe(true)
      expect(state.maps['map-1']?.layers).toEqual(['layer-1'])
      expect(state.maps['map-1']?.basemap).toBe(CartoBasemaps.positron)

      expect(state.maps['map-2']?.isLoaded).toBe(false)
      expect(state.maps['map-2']?.layers).toEqual(['layer-2'])
      expect(state.maps['map-2']?.basemap).toBe(CartoBasemaps.voyager)
    })
  })

  describe('updateViewPort', () => {
    it('should not update viewport when map is not loaded', () => {
      const viewportsBefore = { ...store.getState().viewports }

      store.getState().updateViewPort(mapId)

      const viewportsAfter = store.getState().viewports
      expect(viewportsAfter).toEqual(viewportsBefore)
    })

    it('should not update viewport when no viewState available', () => {
      store.getState().setLoad(mapId, true)

      store.getState().updateViewPort(mapId)

      // Should not throw
      expect(true).toBe(true)
    })

    it('should update viewport when map is loaded and viewState exists', () => {
      store.getState().setLoad(mapId, true)
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          viewState: { longitude: 10, latitude: 20, zoom: 8 },
          width: 800,
          height: 600,
        },
      }

      store.getState().updateViewPort(mapId)

      const viewport = store.getState().viewports[mapId]
      expect(viewport).toBeDefined()
    })
  })

  describe('setLayerHover', () => {
    it('should return early when no deck instance', () => {
      // Should not throw
      expect(() => {
        store.getState().setLayerHover(mapId, 'layer-1', [0, 0])
      }).not.toThrow()
    })

    it('should return early when no viewport', () => {
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance without viewport
        deck: {
          getViewports: vi.fn().mockReturnValue([]),
        },
      }

      expect(() => {
        store.getState().setLayerHover(mapId, 'layer-1', [0, 0])
      }).not.toThrow()
    })

    it('should return early when no widgetManager', () => {
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance without widgetManager
        deck: {
          getViewports: mockGetViewports,
        },
      }

      expect(() => {
        store.getState().setLayerHover(mapId, 'layer-1', [0, 0])
      }).not.toThrow()
    })

    it('should return early when projection returns null', () => {
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([null, null]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      store.getState().maps[mapId]!.instanceRef!.current = {
        deck: {
          getViewports: mockGetViewports,
          // @ts-expect-error - mocking deck instance
          widgetManager: mockWidgetManager,
        },
      }

      store.getState().setLayerHover(mapId, 'layer-1', [0, 0])

      // onHover should not be called
      expect(mockWidgetManager.onHover).not.toHaveBeenCalled()
    })

    it('should return early when projection returns NaN', () => {
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([NaN, NaN]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      store.getState().maps[mapId]!.instanceRef!.current = {
        deck: {
          getViewports: mockGetViewports,
          // @ts-expect-error - mocking deck instance
          widgetManager: mockWidgetManager,
        },
      }

      store.getState().setLayerHover(mapId, 'layer-1', [0, 0])

      expect(mockWidgetManager.onHover).not.toHaveBeenCalled()
    })

    it('should return early when pickMultipleObjects returns empty array', () => {
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      store.getState().maps[mapId]!.instanceRef!.current = {
        deck: {
          getViewports: mockGetViewports,
          // @ts-expect-error - mocking deck instance
          widgetManager: mockWidgetManager,
          pickMultipleObjects: vi.fn().mockReturnValue([]),
        },
      }

      store.getState().setLayerHover(mapId, 'layer-1', [0, 0])

      expect(mockWidgetManager.onHover).not.toHaveBeenCalled()
    })

    it('should use default callback to pick first feature when multiple objects are picked', () => {
      const mockLayer1 = {
        onHover: vi.fn(),
      }
      const mockLayer2 = {
        onHover: vi.fn(),
      }
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const mockPickMultipleObjects = vi.fn().mockReturnValue([
        {
          layer: mockLayer1,
          object: { id: 'feature-1' },
          index: 0,
        },
        {
          layer: mockLayer2,
          object: { id: 'feature-2' },
          index: 1,
        },
      ])

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      store.getState().setLayerHover(mapId, 'layer-1', [10, 20])

      expect(mockPickMultipleObjects).toHaveBeenCalledWith({
        x: 100,
        y: 100,
        radius: 5,
        layerIds: ['layer-1'],
      })
      // Should use first picked object (default behavior)
      expect(mockLayer1.onHover).toHaveBeenCalled()
      expect(mockLayer2.onHover).not.toHaveBeenCalled()
      expect(mockWidgetManager.onHover).toHaveBeenCalled()
    })

    it('should use custom callback to select specific feature from multiple picks', () => {
      const mockLayer1 = {
        onHover: vi.fn(),
      }
      const mockLayer2 = {
        onHover: vi.fn(),
      }
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const pickedObjects = [
        {
          layer: mockLayer1,
          object: { id: 'feature-1', priority: 1 },
          index: 0,
        },
        {
          layer: mockLayer2,
          object: { id: 'feature-2', priority: 2 },
          index: 1,
        },
      ]
      const mockPickMultipleObjects = vi.fn().mockReturnValue(pickedObjects)

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      // Custom callback to select feature with highest priority
      const customCallback = vi.fn((picks: PickingInfo[]) => {
        return picks.reduce(
          (max: PickingInfo | undefined, pick: PickingInfo) =>
            (pick.object as { priority: number }).priority >
            ((max?.object as { priority: number } | undefined)?.priority ?? 0)
              ? pick
              : max,
          undefined,
        )
      })

      store.getState().setLayerHover(mapId, 'layer-1', [10, 20], customCallback)

      expect(customCallback).toHaveBeenCalledWith(pickedObjects)
      // Should use second picked object (custom callback logic)
      expect(mockLayer2.onHover).toHaveBeenCalled()
      expect(mockLayer1.onHover).not.toHaveBeenCalled()
      expect(mockWidgetManager.onHover).toHaveBeenCalled()
    })

    it('should handle callback returning undefined', () => {
      const mockLayer = {
        onHover: vi.fn(),
      }
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const mockPickMultipleObjects = vi.fn().mockReturnValue([
        {
          layer: mockLayer,
          object: { id: 'feature-1' },
          index: 0,
        },
      ])

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      // Callback that filters out all features
      const filterCallback = vi.fn(() => undefined)

      store.getState().setLayerHover(mapId, 'layer-1', [10, 20], filterCallback)

      expect(filterCallback).toHaveBeenCalled()
      // Should not trigger hover when callback returns undefined
      expect(mockLayer.onHover).not.toHaveBeenCalled()
      expect(mockWidgetManager.onHover).not.toHaveBeenCalled()
    })

    it('should pass correct event type to onHover handlers', () => {
      const mockLayer = {
        onHover: vi.fn(),
      }
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const mockPickMultipleObjects = vi.fn().mockReturnValue([
        {
          layer: mockLayer,
          object: { id: 'feature-1' },
          index: 0,
        },
      ])

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      store.getState().setLayerHover(mapId, 'layer-1', [10, 20])

      const layerCallArgs = mockLayer.onHover.mock.calls[0] as unknown[]
      expect(layerCallArgs[0]).toMatchObject({
        x: 100,
        y: 100,
        object: { id: 'feature-1' },
      })
      expect(layerCallArgs[1]).toBeInstanceOf(MouseEvent)
      expect((layerCallArgs[1] as MouseEvent).type).toBe('mouseenter')

      const widgetCallArgs = mockWidgetManager.onHover.mock
        .calls[0] as unknown[]
      expect(widgetCallArgs[1]).toBeInstanceOf(MouseEvent)
      expect((widgetCallArgs[1] as MouseEvent).type).toBe('mouseenter')
    })

    it('should handle callback with complex selection logic', () => {
      const mockLayers = [
        { onHover: vi.fn() },
        { onHover: vi.fn() },
        { onHover: vi.fn() },
      ]
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const pickedObjects = [
        {
          layer: mockLayers[0],
          object: { id: 'feature-1', type: 'point', visible: false },
          index: 0,
        },
        {
          layer: mockLayers[1],
          object: { id: 'feature-2', type: 'line', visible: true },
          index: 1,
        },
        {
          layer: mockLayers[2],
          object: { id: 'feature-3', type: 'polygon', visible: true },
          index: 2,
        },
      ]
      const mockPickMultipleObjects = vi.fn().mockReturnValue(pickedObjects)

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      // Select first visible feature of specific type
      const complexCallback = vi.fn((picks: PickingInfo[]) => {
        return picks.find(
          (pick: PickingInfo) =>
            (pick.object as { visible: boolean; type: string }).visible &&
            (pick.object as { visible: boolean; type: string }).type ===
              'polygon',
        )
      })

      store
        .getState()
        .setLayerHover(mapId, 'layer-1', [10, 20], complexCallback)

      expect(complexCallback).toHaveBeenCalledWith(pickedObjects)
      // Should select the third feature (polygon that is visible)
      expect(mockLayers[2]!.onHover).toHaveBeenCalled()
      expect(mockLayers[0]!.onHover).not.toHaveBeenCalled()
      expect(mockLayers[1]!.onHover).not.toHaveBeenCalled()
    })

    it('should handle layer without onHover method', () => {
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const mockPickMultipleObjects = vi.fn().mockReturnValue([
        {
          layer: {}, // Layer without onHover method
          object: { id: 'feature-1' },
          index: 0,
        },
      ])

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      // Should not throw
      expect(() => {
        store.getState().setLayerHover(mapId, 'layer-1', [10, 20])
      }).not.toThrow()

      // widgetManager should still be called
      expect(mockWidgetManager.onHover).toHaveBeenCalled()
    })

    it('should correctly pass geometry coordinates to viewport.project', () => {
      const mockProject = vi.fn().mockReturnValue([123, 456])
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: mockProject,
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const mockPickMultipleObjects = vi.fn().mockReturnValue([
        {
          layer: { onHover: vi.fn() },
          object: { id: 'feature-1' },
          index: 0,
        },
      ])

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      const geometry: [number, number] = [10.5, 20.5]
      store.getState().setLayerHover(mapId, 'layer-1', geometry)

      expect(mockProject).toHaveBeenCalledWith(geometry)
      expect(mockPickMultipleObjects).toHaveBeenCalledWith({
        x: 123,
        y: 456,
        radius: 5,
        layerIds: ['layer-1'],
      })
    })

    it('should correctly add projected coordinates to picked object', () => {
      const mockLayer = { onHover: vi.fn() }
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([150, 250]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const mockPickMultipleObjects = vi.fn().mockReturnValue([
        {
          layer: mockLayer,
          object: { id: 'feature-1' },
          index: 0,
        },
      ])

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      store.getState().setLayerHover(mapId, 'layer-1', [1.5, 2.5])

      const layerCallArgs = mockLayer.onHover.mock.calls[0] as unknown[]
      expect(layerCallArgs[0]).toMatchObject({
        x: 150,
        y: 250,
        object: { id: 'feature-1' },
      })

      const widgetCallArgs = mockWidgetManager.onHover.mock
        .calls[0] as unknown[]
      expect(widgetCallArgs[0]).toMatchObject({
        x: 150,
        y: 250,
        object: { id: 'feature-1' },
      })
    })

    it('should pass layerId correctly to pickMultipleObjects', () => {
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const mockPickMultipleObjects = vi.fn().mockReturnValue([])

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      store.getState().setLayerHover(mapId, 'my-custom-layer-id', [1, 2])

      expect(mockPickMultipleObjects).toHaveBeenCalledWith({
        x: 100,
        y: 100,
        radius: 5,
        layerIds: ['my-custom-layer-id'],
      })
    })

    it('should maintain default behavior when callback is not provided', () => {
      const mockLayer1 = { onHover: vi.fn() }
      const mockLayer2 = { onHover: vi.fn() }
      const mockGetViewports = vi.fn().mockReturnValue([
        {
          project: vi.fn().mockReturnValue([100, 100]),
        },
      ])
      const mockWidgetManager = { onHover: vi.fn() }
      const mockPickMultipleObjects = vi.fn().mockReturnValue([
        {
          layer: mockLayer1,
          object: { id: 'feature-1' },
          index: 0,
        },
        {
          layer: mockLayer2,
          object: { id: 'feature-2' },
          index: 1,
        },
      ])

      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          getViewports: mockGetViewports,
          widgetManager: mockWidgetManager as never,
          pickMultipleObjects: mockPickMultipleObjects,
        },
      }

      // Call without callback parameter
      store.getState().setLayerHover(mapId, 'layer-1', [10, 20])

      // Should default to first item
      expect(mockLayer1.onHover).toHaveBeenCalled()
      expect(mockLayer2.onHover).not.toHaveBeenCalled()
    })
  })

  describe('clearLayersHover', () => {
    it('should return early when no deck instance', () => {
      expect(() => {
        store.getState().clearLayersHover(mapId)
      }).not.toThrow()
    })

    it('should return early when no widgetManager', () => {
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance without widgetManager
        deck: {},
      }

      expect(() => {
        store.getState().clearLayersHover(mapId)
      }).not.toThrow()
    })

    it('should call widgetManager.onHover with empty PickingInfo', () => {
      const mockWidgetManager = { onHover: vi.fn() }
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          widgetManager: mockWidgetManager,
        },
      }

      store.getState().clearLayersHover(mapId)

      expect(mockWidgetManager.onHover).toHaveBeenCalledWith(
        {},
        expect.any(Object),
      )
    })

    it('should call widgetManager.onHover with mouseleave event type', () => {
      const mockWidgetManager = { onHover: vi.fn() }
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          widgetManager: mockWidgetManager,
        },
      }

      store.getState().clearLayersHover(mapId)

      const callArgs = mockWidgetManager.onHover.mock.calls[0] as unknown[]
      expect(callArgs[1]).toBeInstanceOf(MouseEvent)
      expect((callArgs[1] as MouseEvent).type).toBe('mouseleave')
    })

    it('should handle widgetManager.onHover being undefined', () => {
      store.getState().maps[mapId]!.instanceRef!.current = {
        // @ts-expect-error - mocking deck instance
        deck: {
          widgetManager: {},
        },
      }

      expect(() => {
        store.getState().clearLayersHover(mapId)
      }).not.toThrow()
    })

    it('should clear hover state for non-existent map', () => {
      expect(() => {
        store.getState().clearLayersHover('non-existent-map')
      }).not.toThrow()
    })
  })

  describe('controller edge cases', () => {
    it('should handle merging when current controller is object', () => {
      store.getState().setController(mapId, {
        scrollZoom: true,
        dragPan: false,
      })

      store.getState().setController(mapId, {
        dragRotate: true,
      })

      const controller = store.getState().maps[mapId]?.controller as Record<
        string,
        unknown
      >
      expect(controller.scrollZoom).toBe(true)
      expect(controller.dragPan).toBe(false)
      expect(controller.dragRotate).toBe(true)
    })

    it('should replace controller when setting boolean after object', () => {
      store.getState().setController(mapId, {
        scrollZoom: true,
        dragPan: true,
      })

      store.getState().setController(mapId, false)

      const controller = store.getState().maps[mapId]?.controller
      expect(controller).toBe(false)
    })

    it('should handle setting object after boolean controller', () => {
      store.getState().setController(mapId, true)

      store.getState().setController(mapId, {
        scrollZoom: false,
      })

      const controller = store.getState().maps[mapId]?.controller as Record<
        string,
        unknown
      >
      expect(controller.scrollZoom).toBe(false)
    })
  })

  describe('viewport initialization with viewState prop', () => {
    it('should use viewState when initialViewState not provided', () => {
      const customStore = createMapStore([
        {
          id: 'custom-map',
          basemap: CartoBasemaps.positron,
          initialViewState: undefined,
          viewState: {
            longitude: 50,
            latitude: 60,
            zoom: 12,
          },
        },
      ])

      const state = customStore.getState()
      expect(state.viewStates['custom-map']?.longitude).toBe(50)
      expect(state.viewStates['custom-map']?.latitude).toBe(60)
      expect(state.viewStates['custom-map']?.zoom).toBe(12)
      expect(state.initialViewStates['custom-map']?.longitude).toBe(50)
    })

    it('should prefer initialViewState over viewState when both provided', () => {
      const customStore = createMapStore([
        {
          id: 'custom-map',
          basemap: CartoBasemaps.positron,
          initialViewState: {
            longitude: 10,
            latitude: 20,
            zoom: 5,
          },
          viewState: {
            longitude: 50,
            latitude: 60,
            zoom: 12,
          },
        },
      ])

      const state = customStore.getState()
      expect(state.viewStates['custom-map']?.longitude).toBe(10)
      expect(state.initialViewStates['custom-map']?.longitude).toBe(10)
    })
  })

  describe('spatial filter initialization', () => {
    it('should initialize with provided spatialFilter', () => {
      const spatialFilterInit = {
        'filter-1': {
          type: 'Polygon' as const,
          coordinates: [
            [
              [0, 0],
              [1, 0],
              [1, 1],
              [0, 0],
            ],
          ],
        },
      }

      const customStore = createMapStore([
        {
          id: 'spatial-map',
          basemap: CartoBasemaps.positron,
          initialViewState: { longitude: 0, latitude: 0, zoom: 1 },
          spatialFilter: spatialFilterInit,
        },
      ])

      const state = customStore.getState()
      expect(state.spatialFilter['spatial-map']).toEqual(spatialFilterInit)
    })

    it('should initialize with empty object when no spatialFilter provided', () => {
      const customStore = createMapStore([
        {
          id: 'no-filter-map',
          basemap: CartoBasemaps.positron,
          initialViewState: { longitude: 0, latitude: 0, zoom: 1 },
        },
      ])

      const state = customStore.getState()
      expect(state.spatialFilter['no-filter-map']).toEqual({})
    })
  })
})
