/**
 * Standalone single-job runner used by the multi-file queue.
 * Pure async — no React state — so it is easy to sequence and to unit-test.
 */
import { API_BASE } from '@/lib/config';
import type { TranslationConfig } from './types';

const POLL_INTERVAL_MS = 2000;
const MAX_POLL_FAILURES = 5;

export interface RunnerProgress {
  jobId: string;
  status: string;
  progress: number;
  step: string;
}

export interface RunnerResult {
  jobId: string | null;
  status: 'completed' | 'failed' | 'aborted';
  error?: string;
  fileName: string;
}

export interface RunnerOptions {
  onProgress?: (info: RunnerProgress) => void;
  /** Checked between polls and before submit — the queue sets it on abort. */
  shouldAbort?: () => boolean;
  onJobId?: (jobId: string) => void;
  /** Poll interval override — tests run it at 10ms. */
  pollIntervalMs?: number;
}

function authHeaders(): Record<string, string> {
  const headers: Record<string, string> = {};
  const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null;
  if (token) headers['Authorization'] = `Bearer ${token}`;
  return headers;
}

export async function runTranslationJob(
  file: File,
  config: TranslationConfig,
  opts: RunnerOptions = {},
): Promise<RunnerResult> {
  const { onProgress, shouldAbort, onJobId, pollIntervalMs = POLL_INTERVAL_MS } = opts;

  if (shouldAbort?.()) {
    return { jobId: null, status: 'aborted', fileName: file.name };
  }

  const formData = new FormData();
  formData.append('file', file);
  formData.append('source_lang', config.sourceLang);
  formData.append('target_lang', config.targetLang);
  formData.append('mode', config.mode);
  if (config.mode === 'llm' && config.provider) {
    formData.append('provider', config.provider);
  }
  if (config.pdfMode) formData.append('pdf_mode', config.pdfMode);
  if (config.glossaryId) formData.append('glossary_id', config.glossaryId);
  if (config.translateImages !== undefined) {
    formData.append('translate_images', String(config.translateImages));
  }
  const { useTranslationStore } = await import('@/lib/store');
  const { settings } = useTranslationStore.getState();
  if (settings.systemPrompt?.trim()) {
    formData.append('custom_prompt', settings.systemPrompt.trim());
  }

  // Submit
  let jobId: string | null = null;
  let fileName = file.name;
  const submitRes = await fetch(`${API_BASE}/api/v1/translate`, {
    method: 'POST',
    headers: authHeaders(),
    body: formData,
  });
  if (!submitRes.ok) {
    let message = `HTTP ${submitRes.status}`;
    try {
      const err = await submitRes.json();
      message = err.message || err.error || message;
    } catch { /* not JSON */ }
    return { jobId: null, status: 'failed', error: message, fileName };
  }
  const submitData = await submitRes.json();
  jobId = submitData?.data?.id ?? null;
  fileName = submitData?.data?.file_name || file.name;
  if (!jobId) {
    return { jobId: null, status: 'failed', error: 'No job id returned', fileName };
  }
  onJobId?.(jobId);

  // Poll until terminal
  let failures = 0;
  for (;;) {
    if (shouldAbort?.()) {
      // Best effort: tell the server to stop, then report locally as aborted.
      fetch(`${API_BASE}/api/v1/translations/${jobId}/cancel`, {
        method: 'POST',
        headers: authHeaders(),
      }).catch(() => { /* ignore */ });
      return { jobId, status: 'aborted', fileName };
    }

    await new Promise((r) => setTimeout(r, pollIntervalMs));

    let res: Response;
    try {
      res = await fetch(`${API_BASE}/api/v1/translations/${jobId}`, { headers: authHeaders() });
    } catch {
      failures += 1;
      if (failures >= MAX_POLL_FAILURES) {
        return { jobId, status: 'failed', error: 'Lost connection to translation service.', fileName };
      }
      continue;
    }

    if (res.status === 429) continue; // rate-limited poll — not a failure
    if (!res.ok) {
      if (res.status === 404) {
        return { jobId, status: 'failed', error: 'Translation job not found.', fileName };
      }
      failures += 1;
      if (failures >= MAX_POLL_FAILURES) {
        return { jobId, status: 'failed', error: `HTTP ${res.status}`, fileName };
      }
      continue;
    }

    const data = await res.json();
    const job = data?.data;
    const status = job?.status ?? 'processing';
    const progress = job?.progress_percent ?? 0;
    onProgress?.({ jobId: jobId!, status, progress, step: job?.current_step ?? '' });
    failures = 0;

    if (job?.file_name) fileName = job.file_name;
    if (status === 'completed') {
      return { jobId, status: 'completed', fileName };
    }
    if (status === 'failed' || status === 'cancelled') {
      return { jobId, status: 'failed', error: job?.error_message ?? 'Translation failed', fileName };
    }
  }
}
