import { render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, test } from 'vitest'
import { WidgetNoData } from './no-data'
import { useWidgetStore } from '../stores/widget-store'

describe('WidgetNoData', () => {
  const widgetId = 'test-no-data-widget'

  beforeEach(() => {
    useWidgetStore.getState().clearWidgets()
  })

  describe('when sourceData is empty', () => {
    test('renders NoData UI with empty array', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [],
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(
        screen.getByText(/There are no results for the combination of filters/),
      ).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })

    test('renders NoData UI with null sourceData', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: null,
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })

    test('renders NoData UI with undefined sourceData', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: undefined,
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })

    test('renders NoData UI with array of empty arrays (CategoryWidget pattern)', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [[], []],
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })

    test('renders NoData UI with empty object', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: {},
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })
  })

  describe('when sourceData exists', () => {
    test('renders children with array sourceData', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [{ value: 1 }],
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders children with object sourceData', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: { content: 'Hello' },
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders children with nested array sourceData', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [[{ name: 'A', value: 1 }]],
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders children with primitive sourceData (number)', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: 0,
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders children with primitive sourceData (string)', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: 'test',
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders children with primitive sourceData (boolean)', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: false,
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })
  })

  describe('when loading or fetching', () => {
    test('renders children when isLoading=true even if sourceData is empty', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: true,
        isFetching: false,
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders children when isFetching=true even if sourceData is empty', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: true,
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders children when both isLoading and isFetching are true', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: true,
        isFetching: true,
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })
  })

  describe('customization', () => {
    test('renders custom title', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [],
      })

      render(
        <WidgetNoData id={widgetId} title='No results found'>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No results found')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders custom description', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [],
      })

      render(
        <WidgetNoData id={widgetId} description='Try adjusting your filters'>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Try adjusting your filters')).toBeTruthy()
      expect(
        screen.queryByText(/There are no results for the combination/),
      ).toBeNull()
    })

    test('uses custom isEmpty function', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: { customField: 'value' },
      })

      // Custom isEmpty that treats this sourceData as empty
      const customIsEmpty = (data: unknown) => {
        const d = data as { customField: string }
        return d?.customField === 'value'
      }

      render(
        <WidgetNoData id={widgetId} isEmpty={customIsEmpty}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })

    test('custom isEmpty returning false renders children', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [],
      })

      // Custom isEmpty that treats empty array as having data
      const customIsEmpty = () => false

      render(
        <WidgetNoData id={widgetId} isEmpty={customIsEmpty}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })
  })

  describe('when widget does not exist in store', () => {
    test('treats as empty and shows NoData UI', () => {
      // Widget not initialized in store
      render(
        <WidgetNoData id='non-existent-widget'>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })
  })

  describe('sourceData vs pipeline-transformed data', () => {
    test('renders children when sourceData has data but pipeline data is empty', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [{ value: 1 }, { value: 2 }],
        data: [], // pipeline filtered everything out
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      // Should show children because sourceData is not empty
      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('renders NoData when both sourceData and data are empty', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [],
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })

    test('renders NoData when sourceData is null (API returned nothing)', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: null,
      })

      render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })
  })

  describe('reactivity to store changes', () => {
    test('updates from NoData to content when sourceData arrives', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
      })

      const { rerender } = render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()

      // Update with sourceData
      useWidgetStore.getState().setWidget(widgetId, {
        sourceData: [{ value: 1 }],
      })

      // Force rerender to pick up store changes
      rerender(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })

    test('updates from content to NoData when sourceData becomes empty', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
        sourceData: [{ value: 1 }],
      })

      const { rerender } = render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()

      // Clear sourceData
      useWidgetStore.getState().setWidget(widgetId, { sourceData: [] })

      // Force rerender to pick up store changes
      rerender(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()
      expect(screen.queryByText('Widget Content')).toBeNull()
    })

    test('updates from NoData to content when loading starts', () => {
      useWidgetStore.getState().setWidget(widgetId, {
        isLoading: false,
        isFetching: false,
      })

      const { rerender } = render(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('No data available')).toBeTruthy()

      // Start loading
      useWidgetStore.getState().setWidget(widgetId, { isLoading: true })

      // Force rerender to pick up store changes
      rerender(
        <WidgetNoData id={widgetId}>
          <div>Widget Content</div>
        </WidgetNoData>,
      )

      expect(screen.getByText('Widget Content')).toBeTruthy()
      expect(screen.queryByText('No data available')).toBeNull()
    })
  })
})
