/**
 * enqueueDealTranslation — create per-locale translation_jobs + outbox rows.
 *
 * Called after a deal is approved. Inserts one `translation_jobs` row per
 * target locale and one `outbox` row per job, then calls `enqueueOutbox` to
 * publish the queue message for prompt dispatch.
 *
 * Jobs and outbox rows are written sequentially (neon-http has no interactive
 * transaction). Idempotency is enforced by ON CONFLICT DO NOTHING on translation_jobs.
 *
 * Idempotency: the unique partial index
 * `translation_jobs_deal_locale_active_idx` (on deal_id + target_locale WHERE
 * status IN ('PENDING','RUNNING','PENDING_BUDGET')) prevents duplicates.
 * ON CONFLICT DO NOTHING means re-calling for an already-queued deal is safe.
 */

import type { DrizzleClient } from '@/server/db/client.js';
import { getActiveLanguagesCached } from '@/server/i18n/languages/cache.js';
import { enqueueOutbox } from '@/server/queues/outbox-producer.js';
import {
  findTranslationDeal,
  insertTranslationJob,
  insertTranslationEnqueuedOutbox,
} from '@/server/db/queries/translation/jobs.js';

export interface EnqueueDealTranslationOpts {
  /** Override the target locales. Defaults to all active languages except sourceLanguage. */
  targetLocales?: string[];
  /** Schedule jobs to run at a future time. */
  scheduledAt?: Date;
  /** If true, bypasses the translation memory and forces fresh translation. */
  forceFresh?: boolean;
}

export interface EnqueueDealTranslationResult {
  jobIds: string[];
}

/**
 * Enqueue translation jobs for a deal.
 *
 * @param db        - Drizzle client (injected, not singleton).
 * @param dealId    - UUID of the deal to translate.
 * @param opts      - Optional overrides for target locales, scheduling, and forceFresh.
 * @returns         - Array of created translation_jobs UUIDs.
 */
export async function enqueueDealTranslation(
  db: DrizzleClient,
  dealId: string,
  opts?: EnqueueDealTranslationOpts,
): Promise<EnqueueDealTranslationResult> {
  // 1. Read sourceLanguage for the deal
  const [dealRow] = await findTranslationDeal(db, dealId);

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

  const sourceLanguage = dealRow.sourceLanguage;

  // 2. Determine target locales
  let targetLocales: string[];
  if (opts?.targetLocales && opts.targetLocales.length > 0) {
    // Caller-supplied list — still exclude sourceLanguage
    targetLocales = opts.targetLocales.filter((l) => l !== sourceLanguage);
  } else {
    const activeLanguages = await getActiveLanguagesCached();
    targetLocales = activeLanguages
      .filter((l) => l.isActive && l.code !== sourceLanguage)
      .map((l) => l.code);
  }

  if (targetLocales.length === 0) {
    return { jobIds: [] };
  }

  // 3. Insert translation_jobs + outbox rows sequentially (neon-http safe)
  const jobIds: string[] = [];
  const outboxIds: string[] = [];

  for (const locale of targetLocales) {
    const [job] = await insertTranslationJob(db, {
      dealId,
      targetLocale: locale,
      forceFresh: opts?.forceFresh ?? false,
      scheduledAt: opts?.scheduledAt ?? null,
    });

    if (!job) {
      continue;
    }

    jobIds.push(job.id);

    const [outboxRow] = await insertTranslationEnqueuedOutbox(db, job.id);

    if (outboxRow) {
      outboxIds.push(outboxRow.id);
    }
  }

  // 4. Enqueue outbox messages after DB writes
  for (const outboxId of outboxIds) {
    await enqueueOutbox(outboxId);
  }

  return { jobIds };
}
