import { v4 as uuidv4 } from 'uuid';
import { Job, JobStatus, SearchConfig, OutputConfig, Lead } from '../types';
import { ensureAuth } from './auth';
import { buildSearch } from './search-builder';
import { extractFromSearch } from './extractor';
import { isDailyLimitReached, getRemainingToday } from './anti-detection';
import { ab } from './ab';

// In-memory job store
const jobs = new Map<string, Job>();
const jobLeads = new Map<string, Lead[]>();

// Simple queue — only one job runs at a time
let running = false;
const pending: string[] = [];

/**
 * Create a new scrape job and enqueue it
 */
export function createJob(
  searchConfig: SearchConfig,
  outputConfig: OutputConfig,
  maxProperties: number = 20,
  existingSearchId?: string
): Job {
  const job: Job = {
    id: uuidv4(),
    status: 'pending',
    searchConfig,
    outputConfig,
    searchId: existingSearchId,
    maxProperties,
    createdAt: new Date().toISOString(),
    progress: { total: 0, processed: 0, leads: 0 },
  };

  jobs.set(job.id, job);
  jobLeads.set(job.id, []);
  pending.push(job.id);

  // Process queue
  processQueue();

  return job;
}

/**
 * Get a job by ID
 */
export function getJob(id: string): Job | undefined {
  return jobs.get(id);
}

/**
 * Get leads for a job
 */
export function getJobLeads(id: string): Lead[] {
  return jobLeads.get(id) || [];
}

/**
 * Get all jobs
 */
export function getAllJobs(): Job[] {
  return Array.from(jobs.values()).sort(
    (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
  );
}

/**
 * Process the next job in the queue
 */
async function processQueue(): Promise<void> {
  if (running || pending.length === 0) return;

  const jobId = pending.shift()!;
  const job = jobs.get(jobId);
  if (!job) return;

  // Check daily limit
  if (isDailyLimitReached()) {
    job.status = 'failed';
    job.error = `Daily limit reached. ${getRemainingToday()} properties remaining today.`;
    job.completedAt = new Date().toISOString();
    return;
  }

  running = true;
  job.status = 'running';
  job.startedAt = new Date().toISOString();

  try {
    // Authenticate
    await ensureAuth();

    // Build search if no existing search ID
    if (!job.searchId) {
      job.searchId = await buildSearch(job.searchConfig);
    }

    // Clamp to remaining daily limit
    const remaining = getRemainingToday();
    const effectiveMax = Math.min(job.maxProperties, remaining);

    // Extract leads
    const leads = await extractFromSearch(
      job.searchId!,
      job.outputConfig!,
      effectiveMax,
      (processed: number, total: number, leadCount: number) => {
        job.progress = { total, processed, leads: leadCount };
      }
    );

    // Stamp leads with job ID
    for (const lead of leads) {
      lead.jobId = job.id;
    }

    jobLeads.set(job.id, leads);
    job.status = 'complete';
    job.completedAt = new Date().toISOString();
    job.progress = { total: effectiveMax, processed: effectiveMax, leads: leads.length };

  } catch (err: any) {
    job.status = 'failed';
    job.error = err.message;
    job.completedAt = new Date().toISOString();
  } finally {
    // Close browser
    ab('close');
    running = false;
    // Process next job
    processQueue();
  }
}
