import { useRef } from 'react'

import type { PropsWithChildren } from 'react'

import { act, render, renderHook } from '@testing-library/react'
import { afterEach, describe, expect, it } from 'vitest'

import { UnityThemeProvider, useUnityTheme } from './unity-theme-provider'

afterEach(() => {
  delete document.documentElement.dataset.uyTheme
})

describe('UnityThemeProvider', () => {
  it('sets data-uy-theme on the html element when mounted', () => {
    render(
      <UnityThemeProvider theme="rebrand">
        <div>child</div>
      </UnityThemeProvider>,
    )

    expect(document.documentElement.dataset.uyTheme).toBe('rebrand')
  })

  it('defaults to legacy when no theme prop is provided', () => {
    render(
      <UnityThemeProvider>
        <div>child</div>
      </UnityThemeProvider>,
    )

    expect(document.documentElement.dataset.uyTheme).toBe('legacy')
  })

  it('removes data-uy-theme on unmount', () => {
    const { unmount } = render(
      <UnityThemeProvider theme="rebrand">
        <div>child</div>
      </UnityThemeProvider>,
    )

    expect(document.documentElement.dataset.uyTheme).toBe('rebrand')

    unmount()

    expect(document.documentElement.dataset.uyTheme).toBeUndefined()
  })

  it('renders children unchanged', () => {
    const { getByText } = render(
      <UnityThemeProvider>
        <span>hello world</span>
      </UnityThemeProvider>,
    )

    expect(getByText('hello world')).toBeTruthy()
  })

  describe('target', () => {
    it('sets data-uy-theme on a custom element via CSS selector', () => {
      const container = document.createElement('div')
      container.id = 'theme-root'
      document.body.appendChild(container)

      render(
        <UnityThemeProvider theme="rebrand" target="#theme-root">
          <div>child</div>
        </UnityThemeProvider>,
      )

      expect(container.dataset.uyTheme).toBe('rebrand')
      expect(document.documentElement.dataset.uyTheme).toBeUndefined()

      document.body.removeChild(container)
    })

    it('sets data-uy-theme on a custom element via ref', () => {
      const targetEl = document.createElement('section')
      document.body.appendChild(targetEl)

      function TestComponent() {
        const ref = useRef<HTMLElement>(targetEl)
        return (
          <UnityThemeProvider theme="rebrand" target={ref}>
            <div>child</div>
          </UnityThemeProvider>
        )
      }

      render(<TestComponent />)

      expect(targetEl.dataset.uyTheme).toBe('rebrand')
      expect(document.documentElement.dataset.uyTheme).toBeUndefined()

      document.body.removeChild(targetEl)
    })
  })
})

describe('useUnityTheme', () => {
  it('returns the theme value from the provider', () => {
    const { result } = renderHook(() => useUnityTheme(), {
      wrapper: ({ children }) => (
        <UnityThemeProvider theme="rebrand">{children}</UnityThemeProvider>
      ),
    })

    expect(result.current.theme).toBe('rebrand')
  })

  it('returns "legacy" when used outside a provider', () => {
    const { result } = renderHook(() => useUnityTheme())

    expect(result.current.theme).toBe('legacy')
  })

  it('does not throw when used outside a provider', () => {
    expect(() => renderHook(() => useUnityTheme())).not.toThrow()
  })

  it('exposes setTheme to change the theme at runtime', () => {
    const wrapper = ({ children }: PropsWithChildren) => (
      <UnityThemeProvider theme="legacy">{children}</UnityThemeProvider>
    )

    const { result } = renderHook(() => useUnityTheme(), { wrapper })

    expect(result.current.theme).toBe('legacy')
    expect(document.documentElement.dataset.uyTheme).toBe('legacy')

    act(() => {
      result.current.setTheme('rebrand')
    })

    expect(result.current.theme).toBe('rebrand')
    expect(document.documentElement.dataset.uyTheme).toBe('rebrand')
  })

  it('setTheme is a no-op outside a provider', () => {
    const { result } = renderHook(() => useUnityTheme())

    expect(() => {
      act(() => {
        result.current.setTheme('rebrand')
      })
    }).not.toThrow()

    expect(result.current.theme).toBe('legacy')
  })
})
