import { describe, expect, it } from 'vitest'
import { createI18n, I18nConfigError, t, resolveLocale } from './index'

const i18n = createI18n({
  locales: ['he', 'en'] as const,
  defaultLocale: 'he',
  messages: {
    he: { hello: 'שלום', greet: 'שלום {name}' },
    en: { hello: 'Hello' },
  },
})

describe('t', () => {
  it('returns the string for the locale', () => {
    expect(t(i18n, 'he', 'hello')).toBe('שלום')
    expect(t(i18n, 'en', 'hello')).toBe('Hello')
  })
  it('interpolates vars', () => {
    expect(t(i18n, 'he', 'greet', { name: 'דנה' })).toBe('שלום דנה')
  })
  it('falls back to default locale then key', () => {
    expect(t(i18n, 'en', 'greet')).toBe('שלום {name}') // missing in en -> he
    expect(t(i18n, 'he', 'missing')).toBe('missing')
  })
})

describe('resolveLocale', () => {
  it('prefers path segment', () => {
    const r = new Request('https://x.com/en/about')
    expect(resolveLocale(i18n, r)).toBe('en')
  })
  it('falls back to cookie then accept-language then default', () => {
    expect(resolveLocale(i18n, new Request('https://x.com/', { headers: { cookie: 'locale=en' } }))).toBe('en')
    expect(resolveLocale(i18n, new Request('https://x.com/', { headers: { 'accept-language': 'en-US,en;q=0.9' } }))).toBe('en')
    expect(resolveLocale(i18n, new Request('https://x.com/'))).toBe('he')
  })
})

describe('createI18n', () => {
  it('returns a frozen object', () => {
    expect(Object.isFrozen(i18n)).toBe(true)
  })

  it('throws I18nConfigError when defaultLocale not in locales', () => {
    expect(() =>
      createI18n({
        locales: ['en', 'de'] as const,
        defaultLocale: 'fr' as 'en' | 'de',
        messages: { en: {}, de: {} },
      }),
    ).toThrow(I18nConfigError)
  })

  it('throws I18nConfigError when messages missing a locale entry', () => {
    expect(() =>
      createI18n({
        locales: ['en', 'de'] as const,
        defaultLocale: 'en',
        messages: { en: {} } as Record<'en' | 'de', Record<string, string>>,
      }),
    ).toThrow(I18nConfigError)
  })

  it('error carries offending values', () => {
    let caught: unknown
    try {
      createI18n({
        locales: ['en'] as const,
        defaultLocale: 'fr' as 'en',
        messages: { en: {} },
      })
    } catch (e) {
      caught = e
    }
    expect(caught).toBeInstanceOf(I18nConfigError)
    expect((caught as I18nConfigError).offending).toMatchObject({ defaultLocale: 'fr' })
  })
})
