import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { render } from '@testing-library/react'
import { Provider } from './widget-provider'
import {
  clearAllWidgetStores,
  getWidgetStore,
  hasWidgetStore,
  useWidgetId,
} from '../stores'

beforeEach(() => clearAllWidgetStores())
afterEach(() => clearAllWidgetStores())

function ChildSpy({ onMount }: { onMount: (id: string) => void }) {
  const id = useWidgetId()
  onMount(id)
  return null
}

describe('<Provider>', () => {
  it('creates a store and exposes the id via context', () => {
    const ids: string[] = []
    render(
      <Provider id='p1' data={{ x: 1 }}>
        <ChildSpy onMount={(i) => ids.push(i)} />
      </Provider>,
    )
    expect(ids).toEqual(['p1'])
    expect(hasWidgetStore('p1')).toBe(true)
    const s = getWidgetStore('p1').getState()
    expect(s.rawData).toEqual({ x: 1 })
    // Provider is renderer-agnostic — option pipeline state lives in
    // `<Widget.Echart>`, not on the store.
    expect(
      (s as unknown as { rawOptions?: unknown }).rawOptions,
    ).toBeUndefined()
  })

  it('removes the store on unmount when keepAlive is false (default)', () => {
    const { unmount } = render(
      <Provider id='p2' data={null}>
        <span />
      </Provider>,
    )
    unmount()
    expect(hasWidgetStore('p2')).toBe(false)
  })

  it('preserves the store on unmount when keepAlive is true', () => {
    const { unmount } = render(
      <Provider id='p3' data={null} keepAlive>
        <span />
      </Provider>,
    )
    unmount()
    expect(hasWidgetStore('p3')).toBe(true)
  })

  it('reuses an existing keepAlive store on remount and preserves transformStates', () => {
    const { unmount } = render(
      <Provider id='p4' data={null} keepAlive>
        <span />
      </Provider>,
    )
    getWidgetStore('p4').setState({
      transformStates: { foo: { enabled: false } },
    })
    unmount()
    render(
      <Provider id='p4' data={null} keepAlive>
        <span />
      </Provider>,
    )
    expect(getWidgetStore('p4').getState().transformStates.foo).toEqual({
      enabled: false,
    })
  })

  it('syncs data / isLoading / error / formatter prop changes onto the store', () => {
    const fmt = (n: number): string => `$${n}`
    const { rerender } = render(
      <Provider id='p5' data={1} formatter={fmt}>
        <span />
      </Provider>,
    )
    expect(getWidgetStore('p5').getState().rawData).toBe(1)
    rerender(
      <Provider
        id='p5'
        data={2}
        isLoading
        error={{ message: 'oops' }}
        formatter={fmt}
      >
        <span />
      </Provider>,
    )
    const s = getWidgetStore('p5').getState()
    expect(s.rawData).toBe(2)
    expect(s.isLoading).toBe(true)
    expect(s.error).toEqual({ message: 'oops' })
    expect(s.formatter).toBe(fmt)
  })

  it('descendants see the store on first render (sync registration in lazy init)', () => {
    function Spy() {
      const id = useWidgetId()
      // If the store wasn't registered synchronously this would throw.
      const data = getWidgetStore(id).getState().rawData
      return <span data-testid='data'>{JSON.stringify(data)}</span>
    }
    const { getByTestId } = render(
      <Provider id='p6' data={{ initial: true }}>
        <Spy />
      </Provider>,
    )
    expect(getByTestId('data').textContent).toBe('{"initial":true}')
  })
})
