import { describe, it, expect, beforeEach } from 'vitest'
import { renderHook } from '@testing-library/react'
import { useWidgetModel } from './use-widget-model'
import { useSourceStore } from '../../providers/sources/store'
import { wrapper } from '../../../tests/providers'
import type {
  AllExternalSources,
  GeoJSONSource,
} from '../../providers/sources/types'

describe('useWidgetModel', () => {
  const mockAccessToken = 'test-token-123'

  beforeEach(() => {
    useSourceStore.setState({
      sources: {},
      filters: {},
    })
  })

  describe('single source - function signature', () => {
    it('should return widget model for single source', () => {
      const store = useSourceStore.getState()
      store.setSource<AllExternalSources>('source1', {
        type: 'query',
        data: 'SELECT * FROM table',
        connectionName: 'connection1',
        apiBaseUrl: 'http://api',
      })

      const { result } = renderHook(
        () => useWidgetModel(mockAccessToken, 'source1'),
        { wrapper },
      )

      expect(result.current).toBeDefined()
      expect(result.current).not.toBeNull()
      expect(result.current?.props.accessToken).toBe(mockAccessToken)
    })

    it('should return widget model for geojson source', () => {
      const store = useSourceStore.getState()
      store.setSource<GeoJSONSource>('source1', {
        type: 'geojson',
        data: [],
      })

      const { result } = renderHook(
        () => useWidgetModel(mockAccessToken, 'source1'),
        { wrapper },
      )

      expect(result.current).not.toBeNull()
      expect(result.current?.props.accessToken).toBe(mockAccessToken)
    })

    it('should set access token on widget model', () => {
      const store = useSourceStore.getState()
      store.setSource<AllExternalSources>('source1', {
        type: 'query',
        data: 'SELECT * FROM table',
        connectionName: 'connection1',
        apiBaseUrl: 'http://api',
      })

      const { result } = renderHook(
        () => useWidgetModel(mockAccessToken, 'source1'),
        { wrapper },
      )

      expect(result.current?.props.accessToken).toBe(mockAccessToken)
    })
  })

  describe('multiple sources - function signature', () => {
    it('should return array of widget models for multiple sources', () => {
      const store = useSourceStore.getState()
      store.setSource<AllExternalSources>('source1', {
        type: 'query',
        data: 'SELECT * FROM table1',
        connectionName: 'connection1',
        apiBaseUrl: 'http://api',
      })
      store.setSource<AllExternalSources>('source2', {
        type: 'query',
        data: 'SELECT * FROM table2',
        connectionName: 'connection2',
        apiBaseUrl: 'http://api',
      })

      const { result } = renderHook(
        () => useWidgetModel(mockAccessToken, ['source1', 'source2']),
        { wrapper },
      )

      expect(Array.isArray(result.current)).toBe(true)
      expect(result.current).toHaveLength(2)
      expect(result.current[0]).not.toBeNull()
      expect(result.current[1]).not.toBeNull()
    })

    it('should set access token on all widget models', () => {
      const store = useSourceStore.getState()
      store.setSource<AllExternalSources>('source1', {
        type: 'query',
        data: 'SELECT * FROM table1',
        connectionName: 'connection1',
        apiBaseUrl: 'http://api',
      })
      store.setSource<AllExternalSources>('source2', {
        type: 'query',
        data: 'SELECT * FROM table2',
        connectionName: 'connection2',
        apiBaseUrl: 'http://api',
      })

      const { result } = renderHook(
        () => useWidgetModel(mockAccessToken, ['source1', 'source2']),
        { wrapper },
      )

      expect(result.current[0]?.props.accessToken).toBe(mockAccessToken)
      expect(result.current[1]?.props.accessToken).toBe(mockAccessToken)
    })

    it('should handle multiple sources with different types', () => {
      const store = useSourceStore.getState()
      store.setSource<AllExternalSources>('source1', {
        type: 'query',
        data: 'SELECT * FROM table1',
        connectionName: 'connection1',
        apiBaseUrl: 'http://api',
      })
      store.setSource<GeoJSONSource>('source2', {
        type: 'geojson',
        data: [],
      })

      const { result } = renderHook(
        () => useWidgetModel(mockAccessToken, ['source1', 'source2']),
        { wrapper },
      )

      expect(result.current).toHaveLength(2)
      expect(result.current[0]).not.toBeNull()
      expect(result.current[1]).not.toBeNull()
    })
  })

  describe('single source - object signature', () => {
    it('should accept object signature with sourceId', () => {
      const store = useSourceStore.getState()
      store.setSource<AllExternalSources>('source1', {
        type: 'query',
        data: 'SELECT * FROM table',
        connectionName: 'connection1',
        apiBaseUrl: 'http://api',
      })

      const { result } = renderHook(
        () =>
          useWidgetModel({ accessToken: mockAccessToken, sourceId: 'source1' }),
        { wrapper },
      )

      expect(result.current).not.toBeNull()
      expect(result.current?.props.accessToken).toBe(mockAccessToken)
    })
  })

  describe('multiple sources - object signature', () => {
    it('should accept object signature with sourcesId', () => {
      const store = useSourceStore.getState()
      store.setSource<AllExternalSources>('source1', {
        type: 'query',
        data: 'SELECT * FROM table1',
        connectionName: 'connection1',
        apiBaseUrl: 'http://api',
      })
      store.setSource<AllExternalSources>('source2', {
        type: 'query',
        data: 'SELECT * FROM table2',
        connectionName: 'connection2',
        apiBaseUrl: 'http://api',
      })

      const { result } = renderHook(
        () =>
          useWidgetModel({
            accessToken: mockAccessToken,
            sourcesId: ['source1', 'source2'],
          }),
        { wrapper },
      )

      expect(Array.isArray(result.current)).toBe(true)
      expect(result.current).toHaveLength(2)
    })
  })

  describe('error cases', () => {
    it('should throw error when neither sourceId nor sourcesId provided', () => {
      // This is a type-level error, but we can test runtime behavior
      expect(() => {
        renderHook(
          () =>
            // @ts-expect-error Testing runtime error for missing params
            useWidgetModel({
              accessToken: mockAccessToken,
            }),
          { wrapper },
        )
      }).toThrow('Either sourceId or sourcesId must be provided')
    })
  })

  describe('edge cases', () => {
    it('should handle empty access token', () => {
      const store = useSourceStore.getState()
      store.setSource<AllExternalSources>('source1', {
        type: 'query',
        data: 'SELECT * FROM table',
        connectionName: 'connection1',
        apiBaseUrl: 'http://api',
      })

      const { result } = renderHook(() => useWidgetModel('', 'source1'), {
        wrapper,
      })

      expect(result.current).toBeNull()
    })

    it('should return undefined for non-existent source', () => {
      const { result } = renderHook(
        () => useWidgetModel(mockAccessToken, 'non-existent'),
        { wrapper },
      )

      expect(result.current).toBeUndefined()
    })
  })
})
