import { and, asc, eq, ne } from 'drizzle-orm'
import type { Querier, TransactionalDatabase } from '@platform-modules/db'
import { I18nContentError } from './errors.js'
import type { Language, LocaleDirection, NewLanguage } from './model.js'
import { languages, type I18nContentSchema } from './schema.js'

function rowToLanguage(row: typeof languages.$inferSelect): Language {
  return {
    code: row.code,
    nameNative: row.nameNative,
    nameEnglish: row.nameEnglish,
    direction: row.direction as LocaleDirection | null,
    searchConfig: row.searchConfig,
    isActive: row.isActive,
    isDefault: row.isDefault,
    sortOrder: row.sortOrder,
    createdAt: row.createdAt,
    updatedAt: row.updatedAt,
  }
}

export async function listLanguages(
  q: Querier<I18nContentSchema>,
  opts?: { activeOnly?: boolean },
): Promise<Language[]> {
  const rows = opts?.activeOnly
    ? await q.select().from(languages).where(eq(languages.isActive, true)).orderBy(asc(languages.sortOrder))
    : await q.select().from(languages).orderBy(asc(languages.sortOrder))
  return rows.map(rowToLanguage)
}

export async function getDefaultLocale(q: Querier<I18nContentSchema>): Promise<string> {
  const [row] = await q
    .select({ code: languages.code })
    .from(languages)
    .where(eq(languages.isDefault, true))
    .limit(1)
  if (!row) {
    throw new I18nContentError('NO_DEFAULT_LOCALE', 'no default locale configured')
  }
  return row.code
}

export async function isLocaleActive(q: Querier<I18nContentSchema>, code: string): Promise<boolean> {
  const [row] = await q
    .select({ isActive: languages.isActive })
    .from(languages)
    .where(eq(languages.code, code))
    .limit(1)
  return row?.isActive === true
}

export async function upsertLanguage(
  q: TransactionalDatabase<I18nContentSchema>,
  lang: NewLanguage,
): Promise<void> {
  const now = new Date()

  const upsertRow = async (db: Querier<I18nContentSchema>) => {
    const conflictSet = {
      nameNative: lang.nameNative ?? null,
      nameEnglish: lang.nameEnglish ?? null,
      direction: lang.direction ?? null,
      searchConfig: lang.searchConfig ?? null,
      isActive: lang.isActive,
      sortOrder: lang.sortOrder ?? 0,
      updatedAt: now,
      ...(lang.isDefault !== undefined ? { isDefault: lang.isDefault } : {}),
    }

    await db
      .insert(languages)
      .values({
        code: lang.code,
        nameNative: lang.nameNative ?? null,
        nameEnglish: lang.nameEnglish ?? null,
        direction: lang.direction ?? null,
        searchConfig: lang.searchConfig ?? null,
        isActive: lang.isActive,
        isDefault: lang.isDefault ?? false,
        sortOrder: lang.sortOrder ?? 0,
        updatedAt: now,
      })
      .onConflictDoUpdate({
        target: languages.code,
        set: conflictSet,
      })
  }

  if (lang.isDefault === true) {
    await q.transaction(async (tx) => {
      await tx
        .update(languages)
        .set({ isDefault: false, updatedAt: now })
        .where(and(eq(languages.isDefault, true), ne(languages.code, lang.code)))
      await upsertRow(tx)
    })
  } else {
    await upsertRow(q)
  }
}
