import type { Querier } from '@platform-modules/db'
import { listLanguages, type I18nContentSchema } from '@platform-modules/i18n-content'
import type { IdempotencyStore, JobRegistry } from '@platform-modules/jobs'
import type { SourceTextProvider, StaleUnit, TranslationWorker } from './model.js'
import { translateEntity } from './translate.js'

export const TRANSLATION_SWEEP_JOB = 'i18n.translation.sweep'

export type TranslationWorkerEnv = {
  q: Querier<I18nContentSchema>
  source: SourceTextProvider
  idempotencyStore: IdempotencyStore
  selectStale: (
    q: Querier<I18nContentSchema>,
    args: { locale: string; limit: number },
  ) => Promise<readonly StaleUnit[]>
}

export type CreateTranslationWorkerDeps = TranslationWorkerEnv & {
  registry: JobRegistry<TranslationWorkerEnv>
}

function passthroughSchema() {
  return {
    '~standard': {
      version: 1 as const,
      vendor: 'i18n-translator',
      validate: (value: unknown) => ({ value }),
    },
  }
}

export function idempotencyKey(unit: StaleUnit): string {
  return `${unit.entityType}:${unit.entityId}:${unit.fieldKey}:${unit.locale}:${unit.sourceHash}`
}

function entityBatchKey(unit: StaleUnit): string {
  return `${unit.entityType}:${unit.entityId}:${unit.locale}`
}

async function runSweepInternal(
  env: TranslationWorkerEnv,
  limitPerLocale: number,
): Promise<void> {
  const languages = await listLanguages(env.q, { activeOnly: true })

  for (const language of languages) {
    const staleUnits = await env.selectStale(env.q, {
      locale: language.code,
      limit: limitPerLocale,
    })

    const pendingByKey = new Map<string, StaleUnit>()
    for (const unit of staleUnits) {
      const key = idempotencyKey(unit)
      if (await env.idempotencyStore.seen(key)) continue
      pendingByKey.set(key, unit)
    }

    const batches = new Map<string, StaleUnit[]>()
    for (const unit of pendingByKey.values()) {
      const batchKey = entityBatchKey(unit)
      const group = batches.get(batchKey) ?? []
      group.push(unit)
      batches.set(batchKey, group)
    }

    for (const units of batches.values()) {
      const first = units[0]!
      const unitByField = new Map(units.map((unit) => [unit.fieldKey, unit]))

      try {
        const result = await translateEntity(env.q, {
          entityType: first.entityType,
          entityId: first.entityId,
          fields: units.map((unit) => unit.fieldKey),
          targetLocales: [first.locale],
          source: env.source,
        })

        const localeResult = result.results.find((entry) => entry.locale === first.locale)
        if (!localeResult) continue

        for (const field of localeResult.fields) {
          const unit = unitByField.get(field.fieldKey)
          if (!unit) continue
          if (field.outcome === 'OK' || field.outcome === 'SKIPPED') {
            await env.idempotencyStore.mark(idempotencyKey(unit))
          }
        }
      } catch {
        continue
      }
    }
  }
}

export function createTranslationWorker(deps: CreateTranslationWorkerDeps): TranslationWorker {
  const env: TranslationWorkerEnv = {
    q: deps.q,
    source: deps.source,
    idempotencyStore: deps.idempotencyStore,
    selectStale: deps.selectStale,
  }

  deps.registry.register(
    TRANSLATION_SWEEP_JOB,
    passthroughSchema(),
    async (handlerEnv: TranslationWorkerEnv) => {
      await runSweepInternal(handlerEnv, 100)
    },
  )

  return {
    async runSweep(args = {}) {
      await runSweepInternal(env, args.limitPerLocale ?? 100)
    },
  }
}