import type { Querier } from '@platform-modules/db'
import {
  UnknownLocaleError,
  getTranslationsFor,
  listLanguages,
  type I18nContentSchema,
} from '@platform-modules/i18n-content'
import {
  getEntityTranslationStatusFor,
  type EntityTranslationStatus,
} from '@platform-modules/i18n-translator'
import { product, type CatalogSchema } from './schema.js'

export type ProductRow = typeof product.$inferSelect
export type LocalizedProduct = ProductRow

const PRODUCT_TRANSLATABLE_FIELDS = ['title', 'description'] as const

function productFieldList(fields?: readonly string[]): readonly string[] {
  return fields && fields.length > 0 ? fields : PRODUCT_TRANSLATABLE_FIELDS
}

function coalesceFieldValue(row: ProductRow, field: string, translated: string | undefined): unknown {
  if (translated !== undefined) {
    return translated
  }
  return row[field as keyof ProductRow]
}

async function validateLocales(
  q: Querier<CatalogSchema>,
  locale: string,
  defaultLocale?: string,
): Promise<string | undefined> {
  const languages = await listLanguages(q as unknown as Querier<I18nContentSchema>)
  const active = new Set(languages.filter((language) => language.isActive).map((language) => language.code))
  if (!active.has(locale)) {
    throw new UnknownLocaleError(locale)
  }
  if (defaultLocale !== undefined && !active.has(defaultLocale)) {
    throw new UnknownLocaleError(defaultLocale)
  }
  const resolvedDefault = defaultLocale ?? languages.find((language) => language.isDefault)?.code
  if (resolvedDefault !== undefined && !active.has(resolvedDefault)) {
    throw new UnknownLocaleError(resolvedDefault)
  }
  return resolvedDefault
}

export async function localizeProducts(
  q: Querier<CatalogSchema>,
  rows: readonly ProductRow[],
  args: {
    locale: string
    defaultLocale: string
    fields?: readonly string[]
  },
): Promise<LocalizedProduct[]> {
  if (rows.length === 0) {
    return []
  }

  const fields = productFieldList(args.fields)
  await validateLocales(q, args.locale, args.defaultLocale)

  if (args.locale === args.defaultLocale || fields.length === 0) {
    return rows.map((row) => ({ ...row }))
  }

  const translations = await getTranslationsFor(q as unknown as Querier<I18nContentSchema>, {
    entityType: 'product',
    entityIds: rows.map((row) => row.id),
    locale: args.locale,
    fields,
  })

  return rows.map((row) => {
    const localized: Record<string, unknown> = { ...row }
    const translatedFields = translations.get(row.id) ?? {}
    for (const field of fields) {
      localized[field] = coalesceFieldValue(row, field, translatedFields[field])
    }
    return localized as LocalizedProduct
  })
}

export function productVisibleInLocale(status: EntityTranslationStatus): boolean {
  return status === 'complete'
}

export async function filterVisibleInLocale(
  q: Querier<CatalogSchema>,
  rows: readonly ProductRow[],
  args: {
    locale: string
    fields?: readonly string[]
  },
): Promise<ProductRow[]> {
  if (rows.length === 0) {
    return []
  }

  const fields = productFieldList(args.fields)
  const defaultLocale = await validateLocales(q, args.locale)
  if (args.locale === defaultLocale || fields.length === 0) {
    return [...rows]
  }

  const statuses = await getEntityTranslationStatusFor(q as never, {
    entityType: 'product',
    entityIds: rows.map((row) => row.id),
    locale: args.locale,
    fields,
  })

  return rows.filter((row) => productVisibleInLocale(statuses.get(row.id) ?? 'missing'))
}
