/**
 * state.ts — Translation pipeline status recomputation.
 *
 * `afterTranslate` is called by the queue consumer after each per-locale job
 * completes (success or failure). It recomputes `deals.translation_status` from
 * the ground truth in `deal_translations` and `translation_jobs`.
 *
 * Invariant: this function NEVER touches `deals.deal_state`.
 * Visibility (whether a deal is shown to buyers) is handled solely by the
 * `dealsVisibleWhere` predicate in Plan 1.
 */

import type { DrizzleClient } from '@/server/db/client.js';
import { invalidateCatalog } from '@/server/cache/invalidate.js';
import { deals, dealTranslations, translationJobs } from '@/server/db/schema';
import { getActiveLanguagesCached } from '@/server/i18n/languages/cache';
import { eq } from 'drizzle-orm';
import { setTranslationStatus } from '@/server/db/queries/deals-extra.js';

export type TranslationStatus = 'NOT_STARTED' | 'PENDING' | 'PARTIAL' | 'COMPLETE' | 'FAILED';

/**
 * Recomputes `deals.translation_status` for `dealId` and writes it back.
 *
 * Logic:
 * - COMPLETE  — every target locale has a `deal_translations` row with
 *               status='OK' AND non-empty title AND non-empty description.
 * - PARTIAL   — at least one (but not all) target locales are OK.
 * - FAILED    — no OK translations, and every translation_job for this deal
 *               is in a terminal FAILED state.
 * - PENDING   — no OK translations, and at least one job is still in-flight
 *               (PENDING | RUNNING | PENDING_BUDGET).
 * - NOT_STARTED — no translation rows and no jobs exist yet.
 *
 * @param db     Query-capable database handle (injected for testability).
 * @param dealId UUID of the deal to recompute.
 * @returns      The new status written to the DB.
 */
export async function afterTranslate(
  db: DrizzleClient,
  dealId: string,
): Promise<TranslationStatus> {
  // 1. Resolve target locales (all active languages minus the deal's source).
  const [dealRow] = await db
    .select({ sourceLanguage: deals.sourceLanguage })
    .from(deals)
    .where(eq(deals.id, dealId));

  if (!dealRow) throw new Error(`Deal not found: ${dealId}`);

  const activeLangs = await getActiveLanguagesCached();
  const targets = activeLangs.filter((l) => l.isActive && l.code !== dealRow.sourceLanguage);

  if (targets.length === 0) {
    // No target languages — nothing to translate, treat as complete.
    await setTranslationStatus(db, dealId, 'COMPLETE');
    await invalidateCatalog(db, { scope: 'deal', dealId });
    return 'COMPLETE';
  }

  // 2. Count OK translations (non-empty title + description + status='OK').
  const allTranslations = await db
    .select({
      locale: dealTranslations.locale,
      status: dealTranslations.status,
      title: dealTranslations.title,
      description: dealTranslations.description,
    })
    .from(dealTranslations)
    .where(eq(dealTranslations.dealId, dealId));

  const okLocales = new Set(
    allTranslations
      .filter((t) => t.status === 'OK' && t.title !== '' && t.description !== '')
      .map((t) => t.locale),
  );

  const targetCodes = targets.map((l) => l.code);
  const okCount = targetCodes.filter((code) => okLocales.has(code)).length;

  let next: TranslationStatus;

  if (okCount === targets.length) {
    next = 'COMPLETE';
  } else if (okCount > 0) {
    next = 'PARTIAL';
  } else {
    // 3. No OK translations — inspect jobs to distinguish PENDING / FAILED / NOT_STARTED.
    const jobs = await db
      .select({ status: translationJobs.status })
      .from(translationJobs)
      .where(eq(translationJobs.dealId, dealId));

    if (jobs.length === 0) {
      next = 'NOT_STARTED';
    } else {
      const inFlight = jobs.some((j) =>
        ['PENDING', 'RUNNING', 'PENDING_BUDGET'].includes(j.status),
      );
      if (inFlight) {
        next = 'PENDING';
      } else {
        // All jobs terminal — check if all are FAILED.
        const allFailed = jobs.every((j) => j.status === 'FAILED');
        next = allFailed ? 'FAILED' : 'NOT_STARTED';
      }
    }
  }

  // 4. Write new status — never touches deal_state.
  await setTranslationStatus(db, dealId, next);
  if (next === 'COMPLETE') {
    await invalidateCatalog(db, { scope: 'deal', dealId });
  }

  return next;
}
