import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest'
import { sql } from 'drizzle-orm'
import type { TransactionalDatabase } from '@platform-modules/db'
import { I18nContentError, isI18nContentError } from './errors.js'
import {
  getDefaultLocale,
  isLocaleActive,
  listLanguages,
  upsertLanguage,
} from './languages.js'
import { pushSchema } from './migrate.js'
import { makePgHarness } from './pg-harness.js'
import type { I18nContentSchema } from './schema.js'

const h = await makePgHarness()
const db = h.db as unknown as TransactionalDatabase<I18nContentSchema>

beforeAll(async () => {
  await pushSchema(h.db)
})

beforeEach(async () => {
  await db.execute(sql`DELETE FROM languages`)
})

afterAll(async () => {
  await h.teardown()
})

describe('languages registry', () => {
  it('listLanguages returns rows ordered by sortOrder', async () => {
    await upsertLanguage(db, { code: 'he', isActive: true, sortOrder: 2 })
    await upsertLanguage(db, { code: 'en', isActive: true, sortOrder: 1 })
    await upsertLanguage(db, { code: 'fr', isActive: true, sortOrder: 3 })

    const codes = (await listLanguages(db)).map((l) => l.code)
    expect(codes).toEqual(['en', 'he', 'fr'])
  })

  it('listLanguages with activeOnly excludes inactive locales', async () => {
    await upsertLanguage(db, { code: 'en', isActive: true, sortOrder: 1 })
    await upsertLanguage(db, { code: 'he', isActive: false, sortOrder: 2 })

    const active = await listLanguages(db, { activeOnly: true })
    expect(active.map((l) => l.code)).toEqual(['en'])
  })

  it('getDefaultLocale returns the isDefault row code', async () => {
    await upsertLanguage(db, { code: 'en', isActive: true, isDefault: true, sortOrder: 1 })
    await upsertLanguage(db, { code: 'he', isActive: true, sortOrder: 2 })

    await expect(getDefaultLocale(db)).resolves.toBe('en')
  })

  it('getDefaultLocale throws NO_DEFAULT_LOCALE when none configured', async () => {
    await upsertLanguage(db, { code: 'en', isActive: true, sortOrder: 1 })

    await expect(getDefaultLocale(db)).rejects.toSatisfy((e: unknown) => {
      return isI18nContentError(e) && e.code === 'NO_DEFAULT_LOCALE'
    })
    await expect(getDefaultLocale(db)).rejects.toBeInstanceOf(I18nContentError)
  })

  it('isLocaleActive returns true for an active locale', async () => {
    await upsertLanguage(db, { code: 'en', isActive: true, sortOrder: 1 })

    await expect(isLocaleActive(db, 'en')).resolves.toBe(true)
  })

  it('isLocaleActive returns false for inactive locale', async () => {
    await upsertLanguage(db, { code: 'he', isActive: false, sortOrder: 1 })

    await expect(isLocaleActive(db, 'he')).resolves.toBe(false)
  })

  it('isLocaleActive returns false for unknown code without throwing', async () => {
    await expect(isLocaleActive(db, 'xx')).resolves.toBe(false)
  })

  it('upsertLanguage preserves isDefault when omitted on conflict update', async () => {
    await upsertLanguage(db, { code: 'en', isActive: true, isDefault: true, sortOrder: 1 })
    await upsertLanguage(db, { code: 'en', isActive: true, sortOrder: 5 })

    await expect(getDefaultLocale(db)).resolves.toBe('en')
  })

  it('upsertLanguage preserves one-default invariant when re-pointing default', async () => {
    await upsertLanguage(db, { code: 'en', isActive: true, isDefault: true, sortOrder: 1 })
    await upsertLanguage(db, { code: 'he', isActive: true, isDefault: true, sortOrder: 2 })

    await expect(getDefaultLocale(db)).resolves.toBe('he')

    const all = await listLanguages(db)
    const defaults = all.filter((l) => l.isDefault)
    expect(defaults).toHaveLength(1)
    expect(defaults[0]!.code).toBe('he')

    const en = all.find((l) => l.code === 'en')
    expect(en?.isDefault).toBe(false)
  })
})
