import { afterEach, describe, expect, it, vi } from 'vitest'
import { render, screen, fireEvent, cleanup, act } from '@testing-library/react'
import { renderToString } from 'react-dom/server'
import { hydrateRoot } from 'react-dom/client'
import { createI18n } from '@platform-modules/i18n'
import {
  DirectionProvider,
  I18nProvider,
  useDirection,
  useLocale,
  useT,
  useTranslations,
  LanguageSwitcher,
} from './index'
import { createLocaleStore } from './store'

const i18n = createI18n({
  locales: ['he', 'en'] as const,
  defaultLocale: 'he',
  messages: {
    he: { hello: 'שלום', cart: { title: 'עגלה' } },
    en: { hello: 'Hello', cart: { title: 'Cart' } },
  },
})

function Greeting() {
  const tr = useTranslations()
  return <span>{tr('hello')}</span>
}

describe('i18n-react', () => {
  afterEach(cleanup)

  it('translates via provider locale', () => {
    render(
      <I18nProvider i18n={i18n} locale="en">
        <Greeting />
      </I18nProvider>,
    )
    expect(screen.getByText('Hello')).toBeTruthy()
  })

  it('LanguageSwitcher fires onChange and marks the active locale with aria-current', () => {
    const onChange = vi.fn()
    render(
      <I18nProvider i18n={i18n} locale="he">
        <LanguageSwitcher locales={['he', 'en']} onChange={onChange} />
      </I18nProvider>,
    )
    const buttons = screen.getAllByRole('button')
    const en = buttons.find((b) => b.getAttribute('lang') === 'en')!
    const he = buttons.find((b) => b.getAttribute('lang') === 'he')!
    fireEvent.click(en)
    expect(onChange).toHaveBeenCalledWith('en')
    expect(he.getAttribute('aria-current')).toBe('true')
    expect(en.getAttribute('aria-current')).toBe(null)
    expect(en.textContent).toBe('English')
    expect(he.textContent).toBe('עברית')
  })

  it('falls back to the raw code on a malformed BCP-47 tag instead of throwing', () => {
    expect(() =>
      render(
        <I18nProvider i18n={i18n} locale="he">
          <LanguageSwitcher locales={['en_US']} onChange={vi.fn()} />
        </I18nProvider>,
      ),
    ).not.toThrow()
    expect(screen.getByRole('button').textContent).toBe('en_US')
  })

  it('useT resolves namespaced nested keys', () => {
    function CartTitle() {
      return <span>{useT('cart')('title')}</span>
    }
    render(
      <I18nProvider i18n={i18n} locale="en">
        <CartTitle />
      </I18nProvider>,
    )
    expect(screen.getByText('Cart')).toBeTruthy()
  })

  it('useT memoizes translator for the same namespace and locale', () => {
    const refs: Array<(key: string) => string> = []
    function Probe() {
      const tr = useT('cart')
      refs.push(tr)
      return null
    }
    const { rerender } = render(
      <I18nProvider i18n={i18n} locale="he">
        <Probe />
      </I18nProvider>,
    )
    rerender(
      <I18nProvider i18n={i18n} locale="he">
        <Probe />
      </I18nProvider>,
    )
    expect(refs.length).toBeGreaterThanOrEqual(2)
    expect(refs[refs.length - 1]).toBe(refs[refs.length - 2])
  })

  it('DirectionProvider exposes rtl/ltr for nested components', () => {
    function DirReader() {
      const direction = useDirection()
      return <span data-testid="dir">{direction}</span>
    }
    render(
      <I18nProvider i18n={i18n} locale="he">
        <DirectionProvider>
          <DirReader />
        </DirectionProvider>
      </I18nProvider>,
    )
    expect(screen.getByTestId('dir').textContent).toBe('rtl')
  })

  it('hydration parity: first client paint matches SSR locale (#418 guard)', () => {
    const container = document.createElement('div')
    container.innerHTML = renderToString(
      <I18nProvider i18n={i18n} locale="he">
        <Greeting />
      </I18nProvider>,
    )
    const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
    act(() => {
      hydrateRoot(
        container,
        <I18nProvider i18n={i18n} locale="he">
          <Greeting />
        </I18nProvider>,
      )
    })
    expect(container.textContent).toBe('שלום')
    const hydrationErrors = errorSpy.mock.calls.filter(([msg]) =>
      String(msg).toLowerCase().includes('hydration'),
    )
    expect(hydrationErrors).toHaveLength(0)
    errorSpy.mockRestore()
  })
})

describe('locale store', () => {
  it('shares locale across two subscribers', () => {
    const useStore = createLocaleStore({ defaultLocale: 'he', storageKey: 'test-locale-store' })
    function A() {
      return <span data-testid="a">{useStore((s) => s.locale)}</span>
    }
    function B() {
      const setLocale = useStore((s) => s.setLocale)
      return (
        <button type="button" onClick={() => setLocale('en')}>
          switch
        </button>
      )
    }
    render(
      <>
        <A />
        <B />
      </>,
    )
    expect(screen.getByTestId('a').textContent).toBe('he')
    fireEvent.click(screen.getByRole('button'))
    expect(screen.getByTestId('a').textContent).toBe('en')
  })
})

describe('useLocale', () => {
  afterEach(cleanup)

  it('reads the seeded SSR locale on mount', () => {
    function Reader() {
      return <span data-testid="loc">{useLocale()}</span>
    }
    render(
      <I18nProvider i18n={i18n} locale="en">
        <Reader />
      </I18nProvider>,
    )
    expect(screen.getByTestId('loc').textContent).toBe('en')
  })
})
