import { and, eq, inArray } from 'drizzle-orm'
import type { Querier } from '@platform-modules/db'
import { translationValue, type I18nContentSchema } from '@platform-modules/i18n-content'
import type {
  EntityTranslationStatus,
  GetEntityTranslationStatusArgs,
  GetEntityTranslationStatusForArgs,
} from './model.js'

type TranslationRow = {
  fieldKey: string
  value: string
  status: string
}

function computeStatus(
  requestedFields: readonly string[],
  rows: readonly TranslationRow[],
): EntityTranslationStatus {
  if (requestedFields.length === 0) {
    return 'complete'
  }

  if (rows.length === 0) {
    return 'missing'
  }

  const fieldCounts = new Map<string, number>()
  for (const row of rows) {
    fieldCounts.set(row.fieldKey, (fieldCounts.get(row.fieldKey) ?? 0) + 1)
  }
  for (const [fieldKey, count] of fieldCounts) {
    if (count > 1) {
      console.warn(
        `[i18n-translator] duplicate translation rows for fieldKey '${fieldKey}'; only one will be used`,
      )
    }
  }
  const rowByField = new Map(rows.map((row) => [row.fieldKey, row]))

  for (const fieldKey of requestedFields) {
    const row = rowByField.get(fieldKey)
    if (row && (row.status === 'FAILED' || row.value === '')) {
      return 'failed'
    }
  }

  for (const fieldKey of requestedFields) {
    if (!rowByField.has(fieldKey)) {
      return 'partial'
    }
  }

  for (const fieldKey of requestedFields) {
    const row = rowByField.get(fieldKey)!
    if (row.status === 'STALE') {
      return 'stale'
    }
  }

  const KNOWN_STATUSES = new Set(['OK', 'STALE', 'FAILED'])
  for (const fieldKey of requestedFields) {
    const row = rowByField.get(fieldKey)!
    if (!KNOWN_STATUSES.has(row.status)) {
      console.warn(
        `[i18n-translator] unknown translation status '${row.status}' for fieldKey '${fieldKey}'; treating as failed`,
      )
      return 'failed'
    }
    if (row.status !== 'OK' || row.value === '') {
      return 'failed'
    }
  }

  return 'complete'
}

/**
 * Returns the aggregate translation status for a single entity.
 *
 * @remarks
 * Ownership scoping is the caller's responsibility — the entityId must already
 * be authorised for the calling tenant/user. No tenantId filter is applied here
 * because the translationValue schema does not carry a tenant column; entity-level
 * access control is enforced upstream by the caller.
 */
export async function getEntityTranslationStatus(
  q: Querier<I18nContentSchema>,
  args: GetEntityTranslationStatusArgs,
): Promise<EntityTranslationStatus> {
  if (args.fields.length === 0) {
    return 'complete'
  }

  const rows = await q
    .select({
      fieldKey: translationValue.fieldKey,
      value: translationValue.value,
      status: translationValue.status,
    })
    .from(translationValue)
    .where(
      and(
        eq(translationValue.entityType, args.entityType),
        eq(translationValue.entityId, args.entityId),
        eq(translationValue.locale, args.locale),
        inArray(translationValue.fieldKey, [...args.fields]),
      ),
    )

  return computeStatus(args.fields, rows)
}

/**
 * Returns the aggregate translation status for a batch of entities.
 *
 * @remarks
 * Ownership scoping is the caller's responsibility — all entityIds must already
 * be authorised for the calling tenant/user. No tenantId filter is applied here
 * because the translationValue schema does not carry a tenant column; entity-level
 * access control is enforced upstream by the caller.
 */
export async function getEntityTranslationStatusFor(
  q: Querier<I18nContentSchema>,
  args: GetEntityTranslationStatusForArgs,
): Promise<Map<string, EntityTranslationStatus>> {
  if (args.fields.length === 0) {
    const result = new Map<string, EntityTranslationStatus>()
    for (const entityId of args.entityIds) {
      result.set(entityId, 'complete')
    }
    return result
  }

  if (args.entityIds.length === 0) {
    return new Map()
  }

  const rows = await q
    .select({
      entityId: translationValue.entityId,
      fieldKey: translationValue.fieldKey,
      value: translationValue.value,
      status: translationValue.status,
    })
    .from(translationValue)
    .where(
      and(
        eq(translationValue.entityType, args.entityType),
        inArray(translationValue.entityId, [...args.entityIds]),
        eq(translationValue.locale, args.locale),
        inArray(translationValue.fieldKey, [...args.fields]),
      ),
    )

  const rowsByEntity = new Map<string, TranslationRow[]>()
  for (const row of rows) {
    const bucket = rowsByEntity.get(row.entityId) ?? []
    bucket.push({ fieldKey: row.fieldKey, value: row.value, status: row.status })
    rowsByEntity.set(row.entityId, bucket)
  }

  const result = new Map<string, EntityTranslationStatus>()
  for (const entityId of args.entityIds) {
    result.set(entityId, computeStatus(args.fields, rowsByEntity.get(entityId) ?? []))
  }

  return result
}
