import { describe, it, expect, beforeEach } from 'vitest'
import { useLayerStore } from './store'
import type { Layer } from './types'
import { DataFilterExtension } from '@deck.gl/extensions'
import { GeoJsonLayer } from '@deck.gl/layers'

describe('LayerStore', () => {
  beforeEach(() => {
    // Reset the store before each test
    useLayerStore.setState({
      layers: {},
    })
  })

  describe('setLayer', () => {
    it('should set a new layer with basic properties', () => {
      const layer: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
          visible: true,
        },
        name: 'Test Layer',
      }

      useLayerStore.getState().setLayer('test-layer', layer)

      const storedLayer = useLayerStore.getState().layers['test-layer']
      expect(storedLayer).toBeDefined()
      expect(storedLayer?.type).toBe(GeoJsonLayer)
      expect(storedLayer?.name).toBe('Test Layer')
      expect(storedLayer?.props.id).toBe('test-layer')
      expect(storedLayer?.props.visible).toBe(true)
    })

    it('should merge properties when updating existing layer', () => {
      const initialLayer: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
          visible: true,
          opacity: 0.8,
        },
        name: 'Initial Name',
      }

      const updateLayer: Partial<Layer> = {
        props: {
          visible: false,
        },
        name: 'Updated Name',
      }

      useLayerStore.getState().setLayer('test-layer', initialLayer)
      useLayerStore.getState().setLayer('test-layer', updateLayer)

      const storedLayer = useLayerStore.getState().layers['test-layer']
      expect(storedLayer?.name).toBe('Updated Name')
      expect(storedLayer?.props.visible).toBe(false)
      expect(storedLayer?.props.opacity).toBe(0.8) // Should preserve original opacity
    })

    it('should handle extensions merging without duplicates', () => {
      const extension1 = new DataFilterExtension({ filterSize: 1 })
      const extension2 = new DataFilterExtension({ filterSize: 1 })

      const layer1: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
          extensions: [extension1],
        },
      }

      const layer2: Partial<Layer> = {
        props: {
          extensions: [extension2],
        },
      }

      useLayerStore.getState().setLayer('test-layer', layer1)
      useLayerStore.getState().setLayer('test-layer', layer2)

      const storedLayer = useLayerStore.getState().layers['test-layer']
      // Should only have one extension (duplicates removed via equals check)
      expect(storedLayer?.props.extensions?.length).toBe(1)
    })

    it('should merge multiple different extensions', () => {
      const extension1 = new DataFilterExtension({ filterSize: 1 })
      const extension2 = new DataFilterExtension({ filterSize: 2 })

      const layer1: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
          extensions: [extension1],
        },
      }

      const layer2: Partial<Layer> = {
        props: {
          extensions: [extension2],
        },
      }

      useLayerStore.getState().setLayer('test-layer', layer1)
      useLayerStore.getState().setLayer('test-layer', layer2)

      const storedLayer = useLayerStore.getState().layers['test-layer']
      // Should have both extensions (different filterSize means not equal)
      expect(storedLayer?.props.extensions?.length).toBe(2)
    })

    it('should merge updateTriggers correctly', () => {
      const layer1: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
          updateTriggers: {
            getFillColor: 1,
          },
        },
      }

      const layer2: Partial<Layer> = {
        props: {
          updateTriggers: {
            getLineColor: 2,
          },
        },
      }

      useLayerStore.getState().setLayer('test-layer', layer1)
      useLayerStore.getState().setLayer('test-layer', layer2)

      const storedLayer = useLayerStore.getState().layers['test-layer']
      expect(storedLayer?.props.updateTriggers).toEqual({
        getFillColor: 1,
        getLineColor: 2,
      })
    })

    it('should handle legend merging', () => {
      const layer1: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
        },
        legend: {
          collapsed: false,
          variables: [{ id: 'var1', label: 'Variable 1' }],
        },
      }

      const layer2: Partial<Layer> = {
        legend: {
          collapsed: true,
        },
      }

      useLayerStore.getState().setLayer('test-layer', layer1)
      useLayerStore.getState().setLayer('test-layer', layer2)

      const storedLayer = useLayerStore.getState().layers['test-layer']
      expect(storedLayer?.legend?.collapsed).toBe(true)
      expect(storedLayer?.legend?.variables).toEqual([
        { id: 'var1', label: 'Variable 1' },
      ])
    })

    it('should not create legend if both layers have no legend', () => {
      const layer: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
        },
      }

      useLayerStore.getState().setLayer('test-layer', layer)

      const storedLayer = useLayerStore.getState().layers['test-layer']
      expect(storedLayer?.legend).toBeUndefined()
    })

    it('should handle empty extensions array', () => {
      const layer: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
          extensions: [],
        },
      }

      useLayerStore.getState().setLayer('test-layer', layer)

      const storedLayer = useLayerStore.getState().layers['test-layer']
      expect(storedLayer?.props.extensions).toEqual([])
    })
  })

  describe('removeLayer', () => {
    it('should remove an existing layer', () => {
      const layer: Layer = {
        type: GeoJsonLayer,
        props: {
          id: 'test-layer',
        },
        name: 'Test Layer',
      }

      useLayerStore.getState().setLayer('test-layer', layer)
      expect(useLayerStore.getState().layers['test-layer']).toBeDefined()

      useLayerStore.getState().removeLayer('test-layer')
      expect(useLayerStore.getState().layers['test-layer']).toBeUndefined()
    })

    it('should handle removing non-existent layer gracefully', () => {
      useLayerStore.getState().removeLayer('non-existent-layer')
      expect(
        useLayerStore.getState().layers['non-existent-layer'],
      ).toBeUndefined()
    })
  })

  describe('getLayers', () => {
    it('should return all layers', () => {
      const layer1: Layer = {
        type: GeoJsonLayer,
        props: { id: 'layer-1' },
        name: 'Layer 1',
      }

      const layer2: Layer = {
        type: GeoJsonLayer,
        props: { id: 'layer-2' },
        name: 'Layer 2',
      }

      useLayerStore.getState().setLayer('layer-1', layer1)
      useLayerStore.getState().setLayer('layer-2', layer2)

      const allLayers = useLayerStore.getState().getLayers()
      expect(Object.keys(allLayers)).toHaveLength(2)
      expect(allLayers['layer-1']).toBeDefined()
      expect(allLayers['layer-2']).toBeDefined()
    })

    it('should return empty object when no layers exist', () => {
      const allLayers = useLayerStore.getState().getLayers()
      expect(allLayers).toEqual({})
    })
  })

  describe('getLayer', () => {
    it('should return specific layer by id', () => {
      const layer: Layer = {
        type: GeoJsonLayer,
        props: { id: 'test-layer' },
        name: 'Test Layer',
      }

      useLayerStore.getState().setLayer('test-layer', layer)

      const storedLayer = useLayerStore.getState().getLayer('test-layer')
      expect(storedLayer).toBeDefined()
      expect(storedLayer?.name).toBe('Test Layer')
    })

    it('should return undefined for non-existent layer', () => {
      const storedLayer = useLayerStore.getState().getLayer('non-existent')
      expect(storedLayer).toBeUndefined()
    })
  })
})
