import { afterEach, describe, expect, it, vi } from 'vitest'
import { and, eq } from 'drizzle-orm'
import * as i18nContent from '@platform-modules/i18n-content'
import {
  pushSchema as pushI18nSchema,
  setTranslation,
  translationValue,
  UnknownLocaleError,
  upsertLanguage,
} from '@platform-modules/i18n-content'
import * as i18nTranslator from '@platform-modules/i18n-translator'
import { createPgliteClient } from '../../db/src/postgres/pglite.js'
import { pushSchema as pushCatalogSchema } from './migrate.js'
import { localizeProducts, filterVisibleInLocale, productVisibleInLocale } from './i18n.js'
import { catalogSchema, product } from './schema.js'

type Harness = Awaited<ReturnType<typeof makeHarness>>

async function makeHarness() {
  const db = createPgliteClient({
    schema: { ...catalogSchema, ...i18nContent.i18nContentSchema },
  })
  await pushCatalogSchema(db as never)
  await pushI18nSchema(db)
  await upsertLanguage(db, {
    code: 'en',
    isActive: true,
    isDefault: true,
    sortOrder: 0,
  })
  await upsertLanguage(db, {
    code: 'he',
    isActive: true,
    isDefault: false,
    sortOrder: 1,
  })
  return { db, teardown: async () => {} }
}

async function seedProduct(
  db: Harness['db'],
  values: { slug: string; title: string; description?: string | null },
) {
  const [row] = await db
    .insert(product)
    .values({
      kind: 'physical',
      slug: values.slug,
      title: values.title,
      description: values.description ?? null,
      status: 'active',
      vendorId: null,
      tags: [],
    })
    .returning()
  return row!
}

describe('commerce-catalog i18n', () => {
  let harness: Harness | null = null

  afterEach(async () => {
    vi.restoreAllMocks()
    if (harness) {
      await harness.teardown()
      harness = null
    }
  })

  it('localizeProducts resolves translated fields in one batch, coalesces missing or FAILED fields, serves STALE, and short-circuits for the default locale', async () => {
    harness = await makeHarness()
    const { db } = harness
    const ok = await seedProduct(db, { slug: 'ok', title: 'Base title 1', description: 'Base description 1' })
    const stale = await seedProduct(db, {
      slug: 'stale',
      title: 'Base title 2',
      description: 'Base description 2',
    })

    await setTranslation(db, {
      entityType: 'product',
      entityId: ok.id,
      fieldKey: 'title',
      locale: 'he',
      value: 'כותרת',
    })
    await setTranslation(db, {
      entityType: 'product',
      entityId: stale.id,
      fieldKey: 'title',
      locale: 'he',
      value: 'מיושן',
    })
    await setTranslation(db, {
      entityType: 'product',
      entityId: stale.id,
      fieldKey: 'description',
      locale: 'he',
      value: 'Should fail closed',
    })
    await db
      .update(translationValue)
      .set({ status: 'STALE' })
      .where(and(eq(translationValue.entityId, stale.id), eq(translationValue.fieldKey, 'title')))
    await db
      .update(translationValue)
      .set({ status: 'FAILED' })
      .where(and(eq(translationValue.entityId, stale.id), eq(translationValue.fieldKey, 'description')))

    const getTranslationsForSpy = vi.spyOn(i18nContent, 'getTranslationsFor')

    const localized = await localizeProducts(db as never, [ok, stale], {
      locale: 'he',
      defaultLocale: 'en',
    })

    expect(getTranslationsForSpy).toHaveBeenCalledTimes(1)
    expect(localized).toMatchObject([
      {
        id: ok.id,
        title: 'כותרת',
        description: 'Base description 1',
      },
      {
        id: stale.id,
        title: 'מיושן',
        description: 'Base description 2',
      },
    ])

    getTranslationsForSpy.mockClear()
    await expect(
      localizeProducts(db as never, [ok], {
        locale: 'en',
        defaultLocale: 'en',
      }),
    ).resolves.toEqual([ok])
    expect(getTranslationsForSpy).toHaveBeenCalledTimes(0)
  })

  it('productVisibleInLocale returns true only for complete', () => {
    expect(productVisibleInLocale('complete')).toBe(true)
    expect(productVisibleInLocale('partial')).toBe(false)
    expect(productVisibleInLocale('stale')).toBe(false)
    expect(productVisibleInLocale('failed')).toBe(false)
    expect(productVisibleInLocale('missing')).toBe(false)
  })

  it('filterVisibleInLocale keeps only complete rows and uses one batched rollup query', async () => {
    harness = await makeHarness()
    const { db } = harness

    const complete = await seedProduct(db, { slug: 'complete', title: 'Complete', description: 'Complete desc' })
    const partial = await seedProduct(db, { slug: 'partial', title: 'Partial', description: 'Partial desc' })
    const stale = await seedProduct(db, { slug: 'stale', title: 'Stale', description: 'Stale desc' })
    const failed = await seedProduct(db, { slug: 'failed', title: 'Failed', description: 'Failed desc' })
    const missing = await seedProduct(db, { slug: 'missing', title: 'Missing', description: 'Missing desc' })

    for (const fieldKey of ['title', 'description'] as const) {
      await setTranslation(db, {
        entityType: 'product',
        entityId: complete.id,
        fieldKey,
        locale: 'he',
        value: `${fieldKey}-complete`,
      })
    }

    await setTranslation(db, {
      entityType: 'product',
      entityId: partial.id,
      fieldKey: 'title',
      locale: 'he',
      value: 'title-partial',
    })

    for (const fieldKey of ['title', 'description'] as const) {
      await setTranslation(db, {
        entityType: 'product',
        entityId: stale.id,
        fieldKey,
        locale: 'he',
        value: `${fieldKey}-stale`,
      })
    }
    await db
      .update(translationValue)
      .set({ status: 'STALE' })
      .where(and(eq(translationValue.entityId, stale.id), eq(translationValue.fieldKey, 'description')))

    await setTranslation(db, {
      entityType: 'product',
      entityId: failed.id,
      fieldKey: 'title',
      locale: 'he',
      value: 'title-failed',
    })
    await setTranslation(db, {
      entityType: 'product',
      entityId: failed.id,
      fieldKey: 'description',
      locale: 'he',
      value: '',
    })

    const statusSpy = vi.spyOn(i18nTranslator, 'getEntityTranslationStatusFor')

    const visible = await filterVisibleInLocale(
      db as never,
      [complete, partial, stale, failed, missing],
      { locale: 'he' },
    )

    expect(statusSpy).toHaveBeenCalledTimes(1)
    expect(visible.map((row) => row.id)).toEqual([complete.id])
  })

  it('reuses UnknownLocaleError for inactive locale validation', async () => {
    harness = await makeHarness()
    const { db } = harness
    const row = await seedProduct(db, { slug: 'inactive', title: 'Inactive', description: 'Inactive desc' })

    await expect(
      localizeProducts(db as never, [row], {
        locale: 'fr',
        defaultLocale: 'en',
      }),
    ).rejects.toBeInstanceOf(UnknownLocaleError)
  })
})
