import { describe, it, expect, beforeEach } from 'vitest'
import { createLassoToolsStore } from './store'
import type { LassoToolsProviderProps } from './types'
import { DEFAULT_LASSO_TOOLS_MODES } from '../const'

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

  beforeEach(() => {
    const initProps: LassoToolsProviderProps['values'] = [
      {
        mapId,
        enabled: false,
      },
    ]
    store = createLassoToolsStore(initProps)
  })

  describe('initialization', () => {
    it('should initialize with default props', () => {
      const state = store.getState()
      expect(state.data[mapId]).toBeDefined()
      expect(state.data[mapId]?.enabled).toBe(false)
      expect(state.data[mapId]?.modeSelected).toBe(
        DEFAULT_LASSO_TOOLS_MODES.polygon.value,
      )
      expect(state.data[mapId]?.values).toEqual({})
    })

    it('should initialize with custom enabled value', () => {
      const customStore = createLassoToolsStore([
        {
          mapId: 'custom-map',
          enabled: true,
        },
      ])

      const state = customStore.getState()
      expect(state.data['custom-map']?.enabled).toBe(true)
    })

    it('should initialize with custom modeSelected', () => {
      const customStore = createLassoToolsStore([
        {
          mapId: 'custom-map',
          modeSelected: DEFAULT_LASSO_TOOLS_MODES.rectangle.value,
        },
      ])

      const state = customStore.getState()
      expect(state.data['custom-map']?.modeSelected).toBe(
        DEFAULT_LASSO_TOOLS_MODES.rectangle.value,
      )
    })

    it('should initialize with custom values', () => {
      const customValues = {
        'value-1': {
          id: 'value-1',
          type: 'mask' as const,
          geometry: {
            type: 'Polygon' as const,
            coordinates: [
              [
                [0, 0],
                [1, 0],
                [1, 1],
                [0, 1],
                [0, 0],
              ],
            ],
          },
          properties: {
            label: 'Test Label',
            visible: true,
          },
        },
      }

      const customStore = createLassoToolsStore([
        {
          mapId: 'custom-map',
          values: customValues,
        },
      ])

      const state = customStore.getState()
      expect(state.data['custom-map']?.values).toEqual(customValues)
    })

    it('should initialize with custom modeInstance', () => {
      const customMode = new DEFAULT_LASSO_TOOLS_MODES.rectangle.mode()
      const customStore = createLassoToolsStore([
        {
          mapId: 'custom-map',
          modeInstance: customMode,
        },
      ])

      const state = customStore.getState()
      expect(state.data['custom-map']?.modeInstance).toBe(customMode)
    })

    it('should initialize with custom modes', () => {
      const customModes = {
        ...DEFAULT_LASSO_TOOLS_MODES,
        custom: {
          value: 'custom',
          label: 'Custom',
          icon: 'custom-icon',
          mode: DEFAULT_LASSO_TOOLS_MODES.polygon.mode,
        },
      }

      const customStore = createLassoToolsStore([
        {
          mapId: 'custom-map',
          modes: customModes,
        },
      ])

      const state = customStore.getState()
      expect(state.data['custom-map']?.modes).toEqual(customModes)
    })
  })

  describe('setEnabled', () => {
    it('should enable lasso tools', () => {
      store.getState().setEnabled(mapId, true)

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

    it('should disable lasso tools', () => {
      store.getState().setEnabled(mapId, true)
      store.getState().setEnabled(mapId, false)

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

    it('should toggle enabled state', () => {
      store.getState().setEnabled(mapId, true)
      expect(store.getState().data[mapId]?.enabled).toBe(true)

      store.getState().setEnabled(mapId, false)
      expect(store.getState().data[mapId]?.enabled).toBe(false)

      store.getState().setEnabled(mapId, true)
      expect(store.getState().data[mapId]?.enabled).toBe(true)
    })
  })

  describe('setValue', () => {
    it('should set a new value', () => {
      const value = {
        type: 'mask' as const,
        geometry: {
          type: 'Polygon' as const,
          coordinates: [
            [
              [0, 0],
              [1, 0],
              [1, 1],
              [0, 1],
              [0, 0],
            ],
          ],
        },
        properties: {
          visible: true,
          label: 'Test Label',
        },
      }

      store.getState().setValue(mapId, 'test-value', value)

      const state = store.getState()
      expect(state.data[mapId]?.values['test-value']).toBeDefined()
      expect(state.data[mapId]?.values['test-value']?.id).toBe('test-value')
      expect(state.data[mapId]?.values['test-value']?.type).toBe('mask')
    })

    it('should update an existing value', () => {
      const initialValue = {
        type: 'mask' as const,
        geometry: {
          type: 'Polygon' as const,
          coordinates: [
            [
              [0, 0],
              [1, 0],
              [1, 1],
              [0, 1],
              [0, 0],
            ],
          ],
        },
        properties: {
          visible: true,
          label: 'Test Label',
        },
      }

      store.getState().setValue(mapId, 'test-value', initialValue)

      const updatedValue = {
        properties: {
          visible: false,
          label: 'Updated Label',
        },
      }

      store.getState().setValue(mapId, 'test-value', updatedValue)

      const state = store.getState()
      expect(state.data[mapId]?.values['test-value']?.properties?.visible).toBe(
        false,
      )
      expect(state.data[mapId]?.values['test-value']?.type).toBe('mask')
    })

    it('should delete value when undefined is passed', () => {
      const value = {
        type: 'mask' as const,
        geometry: {
          type: 'Polygon' as const,
          coordinates: [
            [
              [0, 0],
              [1, 0],
              [1, 1],
              [0, 1],
              [0, 0],
            ],
          ],
        },
        properties: {
          visible: true,
          label: 'Test Label',
        },
      }

      store.getState().setValue(mapId, 'test-value', value)
      expect(store.getState().data[mapId]?.values['test-value']).toBeDefined()

      store.getState().setValue(mapId, 'test-value', undefined)
      expect(store.getState().data[mapId]?.values['test-value']).toBeUndefined()
    })

    it('should handle multiple values', () => {
      const value1 = {
        type: 'mask' as const,
        geometry: {
          type: 'Polygon' as const,
          coordinates: [
            [
              [0, 0],
              [1, 0],
              [1, 1],
              [0, 1],
              [0, 0],
            ],
          ],
        },
        properties: {
          visible: true,
          label: 'Test Label',
        },
      }

      const value2 = {
        type: 'mask' as const,
        geometry: {
          type: 'Polygon' as const,
          coordinates: [
            [
              [2, 2],
              [3, 2],
              [3, 3],
              [2, 3],
              [2, 2],
            ],
          ],
        },
        properties: {
          visible: false,
          label: 'Test Label',
        },
      }

      store.getState().setValue(mapId, 'value-1', value1)
      store.getState().setValue(mapId, 'value-2', value2)

      const state = store.getState()
      expect(Object.keys(state.data[mapId]?.values ?? {})).toHaveLength(2)
      expect(state.data[mapId]?.values['value-1']).toBeDefined()
      expect(state.data[mapId]?.values['value-2']).toBeDefined()
    })
  })

  describe('setVisibility', () => {
    beforeEach(() => {
      const value = {
        type: 'mask' as const,
        geometry: {
          type: 'Polygon' as const,
          coordinates: [
            [
              [0, 0],
              [1, 0],
              [1, 1],
              [0, 1],
              [0, 0],
            ],
          ],
        },
        properties: {
          visible: true,
          label: 'Test Label',
        },
      }

      store.getState().setValue(mapId, 'test-value', value)
    })

    it('should update visibility to false', () => {
      store.getState().setVisibility(mapId, 'test-value', false)

      const state = store.getState()
      expect(state.data[mapId]?.values['test-value']?.properties?.visible).toBe(
        false,
      )
    })

    it('should update visibility to true', () => {
      store.getState().setVisibility(mapId, 'test-value', false)
      store.getState().setVisibility(mapId, 'test-value', true)

      const state = store.getState()
      expect(state.data[mapId]?.values['test-value']?.properties?.visible).toBe(
        true,
      )
    })

    it('should toggle visibility', () => {
      expect(
        store.getState().data[mapId]?.values['test-value']?.properties?.visible,
      ).toBe(true)

      store.getState().setVisibility(mapId, 'test-value', false)
      expect(
        store.getState().data[mapId]?.values['test-value']?.properties?.visible,
      ).toBe(false)

      store.getState().setVisibility(mapId, 'test-value', true)
      expect(
        store.getState().data[mapId]?.values['test-value']?.properties?.visible,
      ).toBe(true)
    })
  })

  describe('setModeSelected', () => {
    it('should update mode to rectangle', () => {
      store
        .getState()
        .setModeSelected(mapId, DEFAULT_LASSO_TOOLS_MODES.rectangle.value)

      const state = store.getState()
      expect(state.data[mapId]?.modeSelected).toBe(
        DEFAULT_LASSO_TOOLS_MODES.rectangle.value,
      )
    })

    it('should update mode to polygon', () => {
      store
        .getState()
        .setModeSelected(mapId, DEFAULT_LASSO_TOOLS_MODES.rectangle.value)
      store
        .getState()
        .setModeSelected(mapId, DEFAULT_LASSO_TOOLS_MODES.polygon.value)

      const state = store.getState()
      expect(state.data[mapId]?.modeSelected).toBe(
        DEFAULT_LASSO_TOOLS_MODES.polygon.value,
      )
    })

    it('should handle mode selection changes', () => {
      const modes = [
        DEFAULT_LASSO_TOOLS_MODES.polygon.value,
        DEFAULT_LASSO_TOOLS_MODES.rectangle.value,
        DEFAULT_LASSO_TOOLS_MODES.circle.value,
      ]

      modes.forEach((mode) => {
        store.getState().setModeSelected(mapId, mode)
        expect(store.getState().data[mapId]?.modeSelected).toBe(mode)
      })
    })
  })

  describe('setModeInstance', () => {
    it('should update mode instance', () => {
      const newMode = new DEFAULT_LASSO_TOOLS_MODES.rectangle.mode()

      store.getState().setModeInstance(mapId, newMode)

      const state = store.getState()
      expect(state.data[mapId]?.modeInstance).toBe(newMode)
    })

    it('should replace existing mode instance', () => {
      const mode1 = new DEFAULT_LASSO_TOOLS_MODES.polygon.mode()
      const mode2 = new DEFAULT_LASSO_TOOLS_MODES.rectangle.mode()

      store.getState().setModeInstance(mapId, mode1)
      expect(store.getState().data[mapId]?.modeInstance).toBe(mode1)

      store.getState().setModeInstance(mapId, mode2)
      expect(store.getState().data[mapId]?.modeInstance).toBe(mode2)
    })
  })

  describe('setSelectedId / setEditing', () => {
    it('should initialize selectedId=null and editing=false', () => {
      const state = store.getState()
      expect(state.data[mapId]?.selectedId).toBeNull()
      expect(state.data[mapId]?.editing).toBe(false)
    })

    it('should set and clear the selected id', () => {
      store.getState().setSelectedId(mapId, 'geom-1')
      expect(store.getState().data[mapId]?.selectedId).toBe('geom-1')

      store.getState().setSelectedId(mapId, null)
      expect(store.getState().data[mapId]?.selectedId).toBeNull()
    })

    it('should toggle the editing flag', () => {
      store.getState().setEditing(mapId, true)
      expect(store.getState().data[mapId]?.editing).toBe(true)

      store.getState().setEditing(mapId, false)
      expect(store.getState().data[mapId]?.editing).toBe(false)
    })

    it('should clear selectedId and editing when the tool is disabled', () => {
      store.getState().setEnabled(mapId, true)
      store.getState().setSelectedId(mapId, 'geom-1')
      store.getState().setEditing(mapId, true)

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

      const state = store.getState()
      expect(state.data[mapId]?.selectedId).toBeNull()
      expect(state.data[mapId]?.editing).toBe(false)
    })
  })

  describe('setMaskLayerIds', () => {
    it('should initialize maskLayerIds as null (all layers)', () => {
      expect(store.getState().data[mapId]?.maskLayerIds).toBeNull()
    })

    it('should set and clear the masked layer ids', () => {
      store.getState().setMaskLayerIds(mapId, ['layer-a'])
      expect(store.getState().data[mapId]?.maskLayerIds).toEqual(['layer-a'])

      store.getState().setMaskLayerIds(mapId, null)
      expect(store.getState().data[mapId]?.maskLayerIds).toBeNull()
    })
  })

  describe('multiple maps', () => {
    it('should handle multiple map instances', () => {
      const multiMapStore = createLassoToolsStore([
        { mapId: 'map-1' },
        { mapId: 'map-2' },
      ])

      const state = multiMapStore.getState()
      expect(state.data['map-1']).toBeDefined()
      expect(state.data['map-2']).toBeDefined()
    })

    it('should update state independently for each map', () => {
      const multiMapStore = createLassoToolsStore([
        { mapId: 'map-1' },
        { mapId: 'map-2' },
      ])

      multiMapStore.getState().setEnabled('map-1', true)
      multiMapStore.getState().setEnabled('map-2', false)

      const state = multiMapStore.getState()
      expect(state.data['map-1']?.enabled).toBe(true)
      expect(state.data['map-2']?.enabled).toBe(false)
    })

    it('should handle values independently for each map', () => {
      const multiMapStore = createLassoToolsStore([
        { mapId: 'map-1' },
        { mapId: 'map-2' },
      ])

      const value = {
        type: 'mask' as const,
        geometry: {
          type: 'Polygon' as const,
          coordinates: [
            [
              [0, 0],
              [1, 0],
              [1, 1],
              [0, 1],
              [0, 0],
            ],
          ],
        },
        properties: {
          visible: true,
          label: 'Test Label',
        },
      }

      multiMapStore.getState().setValue('map-1', 'value-1', value)
      multiMapStore.getState().setValue('map-2', 'value-2', value)

      const state = multiMapStore.getState()
      expect(state.data['map-1']?.values['value-1']).toBeDefined()
      expect(state.data['map-1']?.values['value-2']).toBeUndefined()
      expect(state.data['map-2']?.values['value-1']).toBeUndefined()
      expect(state.data['map-2']?.values['value-2']).toBeDefined()
    })
  })
})
