import { describe, it, expect, vi } from 'vitest'
import { WidgetGeoJsonSource } from './widget-geojson-source'
import type { Feature } from 'geojson'

// Mock the worker
vi.mock('./widget-geojson-worker?worker&inline', () => ({
  default: vi.fn(),
}))

describe('WidgetGeoJsonSource', () => {
  const mockFeatures: Feature[] = [
    {
      type: 'Feature',
      geometry: { type: 'Point', coordinates: [0, 0] },
      properties: { category: 'A', value: 10 },
    },
    {
      type: 'Feature',
      geometry: { type: 'Point', coordinates: [1, 1] },
      properties: { category: 'B', value: 20 },
    },
    {
      type: 'Feature',
      geometry: { type: 'Point', coordinates: [2, 2] },
      properties: { category: 'A', value: 30 },
    },
  ]

  describe('constructor', () => {
    it('should create instance with worker disabled', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      expect(source).toBeDefined()
      // @ts-expect-error - accessing private property for test
      expect(source._workerEnabled).toBe(false)
      // @ts-expect-error - accessing private property for test
      expect(source._localImpl).not.toBeNull()
    })

    it('should use local implementation when worker is disabled', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      // @ts-expect-error - accessing private property for test
      expect(source._localImpl).toBeDefined()
      // @ts-expect-error - accessing private property for test
      expect(source._workerImpl).toBeNull()
    })

    it('should handle data as array', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      // @ts-expect-error - accessing private property for test
      expect(source._localImpl).not.toBeNull()
    })
  })

  describe('destroy', () => {
    it('should clean up local implementation', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      // @ts-expect-error - accessing private property for test
      const localImpl = source._localImpl
      expect(localImpl).not.toBeNull()

      source.destroy()

      // @ts-expect-error - accessing private property for test
      expect(source._localImpl).toBeNull()
    })

    it('should be callable multiple times', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      expect(() => {
        source.destroy()
        source.destroy()
      }).not.toThrow()
    })
  })

  describe('with minimal properties', () => {
    it('should create source with just data', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      expect(source).toBeDefined()
    })

    it('should create source with filters', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
        filters: {
          category: {
            in: {
              owner: 'test',
              values: ['A'],
            },
          },
        },
      })

      expect(source).toBeDefined()
    })
  })

  describe('worker disabled mode', () => {
    it('should have worker disabled when explicitly set', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      // @ts-expect-error - accessing private property for test
      expect(source._workerEnabled).toBe(false)
    })

    it('should use local impl when worker disabled', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      // @ts-expect-error - accessing private property for test
      expect(source._localImpl).not.toBeNull()
      // @ts-expect-error - accessing private property for test
      expect(source._workerImpl).toBeNull()
    })
  })

  describe('getCategories', () => {
    it('should call local implementation when worker disabled', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getCategories({
        column: 'category',
        operation: 'count',
      })

      expect(result).toBeDefined()
    })

    it('should handle operation parameter', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getCategories({
        column: 'category',
        operation: 'count',
      })

      expect(result).toBeDefined()
    })
  })

  describe('getFormula', () => {
    it('should calculate formula with local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getFormula({
        column: 'value',
        operation: 'sum',
      })

      expect(result).toBeDefined()
    })

    it('should handle count operation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getFormula({
        column: 'value',
        operation: 'count',
      })

      expect(result).toBeDefined()
    })
  })

  describe('getHistogram', () => {
    it('should generate histogram with local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getHistogram({
        column: 'value',
        ticks: [5],
      })

      expect(result).toBeDefined()
    })

    it('should handle operation parameter', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getHistogram({
        column: 'value',
        ticks: [3],
        operation: 'avg',
      })

      expect(result).toBeDefined()
    })
  })

  describe('getRange', () => {
    it('should calculate range with local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getRange({
        column: 'value',
      })

      expect(result).toBeDefined()
    })
  })

  describe('getScatter', () => {
    it('should generate scatter data with local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getScatter({
        xAxisColumn: 'value',
        yAxisColumn: 'value',
      })

      expect(result).toBeDefined()
    })
  })

  describe('getTable', () => {
    it('should get table data with local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getTable({
        columns: ['category', 'value'],
      })

      expect(result).toBeDefined()
      expect(Array.isArray(result.rows)).toBe(true)
    })

    it('should handle pagination parameters', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getTable({
        columns: ['category'],
        offset: 0,
        limit: 2,
      })

      expect(result).toBeDefined()
      expect(result.rows.length).toBeLessThanOrEqual(2)
    })
  })

  describe('getTimeSeries', () => {
    it('should generate time series with local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getTimeSeries({
        column: 'value',
        operation: 'sum',
        stepSize: 'day',
      })

      expect(result).toBeDefined()
    })
  })

  describe('getFeatures', () => {
    it('should throw error for unsupported operation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      await expect(source.getFeatures()).rejects.toThrow(
        'getFeatures not supported for geojson',
      )
    })
  })

  describe('getExtent', () => {
    it('should calculate extent with local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getExtent({})

      expect(result).toBeDefined()
    })
  })

  describe('getAggregations', () => {
    it('should get aggregations with local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      const result = await source.getAggregations({
        aggregations: [
          {
            column: 'value',
            operation: 'sum',
            alias: 'total_value',
          },
        ],
      })

      expect(result).toBeDefined()
    })
  })

  describe('error handling', () => {
    it('should handle errors in local implementation', async () => {
      const source = new WidgetGeoJsonSource({
        data: [],
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      // Should not throw on empty data
      const result = await source.getCategories({
        column: 'nonexistent',
        operation: 'count',
      })

      expect(result).toBeDefined()
    })
  })

  describe('data formats', () => {
    it('should handle feature collection', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      expect(source).toBeDefined()
    })
  })

  describe('props handling', () => {
    it('should pass through connection props', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
      })

      expect(source.props.connectionName).toBe('test-connection')
      expect(source.props.accessToken).toBe('test-token')
    })

    it('should handle filters in props', () => {
      const source = new WidgetGeoJsonSource({
        data: mockFeatures,
        accessToken: 'test-token',
        connectionName: 'test-connection',
        widgetWorker: false,
        filters: {
          category: {
            in: {
              owner: 'test',
              values: ['A'],
            },
          },
        },
      })

      expect(source.props.filters).toBeDefined()
    })
  })
})
