// backend/src/jobs/codex-image-worker.js
// BullMQ worker for Codex image generation jobs.
// Concurrency configurable; serialized per-user via job grouping.

import { Worker, Queue } from 'bullmq';
import { generate, CodexError } from '../services/codex-image.js';
import { query } from '../utils/db.js';
import { logger } from '../utils/logger.js';

const REDIS_URL       = process.env.REDIS_URL || 'redis://localhost:6379';
const CONCURRENCY     = parseInt(process.env.CODEX_CONCURRENCY || '2', 10);
const QUEUE_NAME      = 'codex-images';

const connection = { url: REDIS_URL };

// ─── Queue (exported for enqueueing) ─────────────────────────────────────────

export const codexImageQueue = new Queue(QUEUE_NAME, { connection });

// ─── Enqueue helper ───────────────────────────────────────────────────────────

export async function enqueueImageGeneration(userId, { prompt, fileName, nicheId, relatedScriptId } = {}) {
  const job = await codexImageQueue.add(
    'generate-image',
    { userId, prompt, fileName, nicheId, relatedScriptId },
    {
      attempts: 2,
      backoff: { type: 'fixed', delay: 10000 },
      removeOnComplete: { count: 100 },
      removeOnFail:     { count: 50 },
      // Serialize per-user: only one codex job per user at a time
      jobId: `codex-${userId}-${Date.now()}`,
    }
  );
  return job.id;
}

// ─── Worker ───────────────────────────────────────────────────────────────────

export function startCodexImageWorker() {
  const worker = new Worker(
    QUEUE_NAME,
    async (job) => {
      const { userId, prompt, fileName, relatedScriptId } = job.data;

      logger.info(`Codex image job started`, { jobId: job.id, userId });
      await job.updateProgress(10);

      // Generate the image via CodexImageService
      const result = await generate({ userId, prompt, fileName });
      await job.updateProgress(80);

      // Save to generated_content so it appears in Content Library
      const title = fileName
        ? fileName.replace(/\.png$/i, '').replace(/_/g, ' ')
        : `Generated image ${new Date().toISOString().slice(0, 10)}`;

      const metadata = {
        source: 'codex',
        relatedScriptId: relatedScriptId || null,
        generatedAt: new Date().toISOString(),
      };

      const { rows } = await query(
        `INSERT INTO generated_content
           (user_id, content_type, title, body, file_path, metadata)
         VALUES ($1, 'image', $2, $3, $4, $5)
         RETURNING id`,
        [
          userId,
          title,
          `AI-generated image: ${prompt.slice(0, 200)}`,
          result.path,
          JSON.stringify(metadata),
        ]
      );

      await job.updateProgress(100);
      logger.info(`Codex image job completed`, { jobId: job.id, userId, contentId: rows[0].id });

      return {
        contentId: rows[0].id,
        fileName:  result.fileName,
        path:      result.path,
      };
    },
    {
      connection,
      concurrency: CONCURRENCY,
      // Graceful shutdown
      lockDuration:  CODEX_TIMEOUT_MS + 30000,
    }
  );

  worker.on('failed', async (job, err) => {
    logger.error(`Codex image job failed`, {
      jobId: job?.id,
      userId: job?.data?.userId,
      error: err.message,
      code: err.code,
    });
  });

  worker.on('error', (err) => {
    logger.error('Codex image worker error', { error: err.message });
  });

  logger.info(`Codex image worker started (concurrency: ${CONCURRENCY})`);
  return worker;
}

// Get job status (for polling from frontend)
export async function getJobStatus(jobId) {
  const job = await codexImageQueue.getJob(jobId);
  if (!job) return { status: 'not_found' };

  const state    = await job.getState();
  const progress = job.progress || 0;

  return {
    status:   state,               // waiting | active | completed | failed
    progress: progress,
    result:   state === 'completed' ? job.returnvalue : null,
    error:    state === 'failed'    ? job.failedReason : null,
  };
}

// Pull CODEX_TIMEOUT_MS at module level for lockDuration
const CODEX_TIMEOUT_MS = parseInt(process.env.CODEX_TIMEOUT_MS || '180000', 10);
