import { Router } from 'express';
import { db, save, uid } from '../store';
import { requireAuth, publicUser, AuthedRequest } from '../auth';
import { remediatePdf } from '../pdf';
import { renderCompletionEmail, renderTrialResultEmail, sendEmail } from '../emails';
import { PdfDocument, RemediationJob } from '../types';

const router = Router();

/**
 * Start an AI remediation job.
 * Body: { documentIds: string[], allowPartial?: boolean }
 * Responses:
 *  - 402 { code: 'PAGE_LIMIT' }  when no pages remain on the plan
 *  - 409 { code: 'PARTIAL_REQUIRED', coveredPages } when the plan covers only part
 *    of the requested pages and allowPartial was not set
 *  - 200 { job } otherwise
 */
router.post('/start', requireAuth, (req: AuthedRequest, res) => {
  const user = req.user!;
  const { documentIds, allowPartial } = req.body || {};
  const ids: string[] = Array.isArray(documentIds) ? documentIds : [];
  const docs = db.documents.filter(
    (d) => d.userId === user.id && ids.includes(d.id) && d.status === 'ready'
  );
  if (docs.length === 0) {
    res.status(400).json({ error: 'Add at least one PDF (uploaded or scanned) to start remediation.' });
    return;
  }

  const totalPages = docs.reduce((s, d) => s + d.pages, 0);
  if (user.pagesRemaining <= 0) {
    res.status(402).json({
      code: 'PAGE_LIMIT',
      error: "You've used all pages included in your current plan.",
      totalPages,
      pagesRemaining: 0,
    });
    return;
  }
  if (totalPages > user.pagesRemaining && !allowPartial) {
    res.status(409).json({
      code: 'PARTIAL_REQUIRED',
      error: 'Your plan covers only part of these pages.',
      totalPages,
      pagesRemaining: user.pagesRemaining,
    });
    return;
  }

  // Deduct pages, allocating the remaining balance across documents in order.
  let budget = Math.min(totalPages, user.pagesRemaining);
  const allocations = new Map<string, number>();
  for (const doc of docs) {
    const take = Math.min(doc.pages, budget);
    allocations.set(doc.id, take);
    budget -= take;
  }
  const jobDocs = docs.filter((d) => (allocations.get(d.id) ?? 0) > 0);
  const jobPages = jobDocs.reduce((s, d) => s + (allocations.get(d.id) ?? 0), 0);
  const partial = jobPages < totalPages;

  user.pagesRemaining -= jobPages;
  for (const doc of jobDocs) doc.status = 'processing';

  const job: RemediationJob = {
    id: uid(),
    userId: user.id,
    documentIds: jobDocs.map((d) => d.id),
    totalPages: jobPages,
    progress: 0,
    status: 'processing',
    partial,
    createdAt: new Date().toISOString(),
  };
  db.jobs.push(job);
  save();

  runJob(job, allocations).catch((err) => console.error('[job] failed', err));

  res.json({ job, user: publicUser(user) });
});

async function runJob(job: RemediationJob, allocations: Map<string, number>): Promise<void> {
  const user = db.users.find((u) => u.id === job.userId)!;
  const docs = job.documentIds
    .map((id) => db.documents.find((d) => d.id === id))
    .filter((d): d is PdfDocument => Boolean(d));

  // Simulated AI pipeline progress: ~8 seconds spread across the docs.
  const stepMs = Math.max(400, Math.min(1200, 8000 / Math.max(job.totalPages, 1)));
  for (let p = 5; p < 95; p += 6) {
    job.progress = p;
    save();
    await new Promise((r) => setTimeout(r, stepMs));
  }

  for (const doc of docs) {
    const pagesForDoc = allocations.get(doc.id) ?? doc.pages;
    const outName = doc.name.replace(/\.pdf$/i, '') + '-remediated.pdf';
    try {
      if (doc.storagePath) {
        const { outPath, pages, engine } = await remediatePdf(doc.storagePath, `${job.id}-${outName}`, pagesForDoc);
        doc.remediatedPath = outPath;
        doc.remediatedPages = pages;
        console.log(`[job] "${doc.name}" remediated via ${engine} (${pages} pages)`);
      } else {
        doc.remediatedPages = pagesForDoc;
      }
      doc.remediatedName = outName;
      doc.remediatedAt = new Date().toISOString();
      doc.status = 'remediated';
    } catch (err) {
      console.error('[job] remediation failed for', doc.name, err);
      doc.status = 'error';
    }
  }

  job.progress = 100;
  job.status = 'completed';
  job.completedAt = new Date().toISOString();
  save();

  // Notification emails (designs 14 & 15), written to the backend outbox.
  const done = docs.filter((d) => d.status === 'remediated');
  if (job.partial && done.length === 1 && user.planId === 'free') {
    const doc = done[0];
    const remediatedNow = doc.remediatedPages ?? 0;
    const remaining = doc.pages - remediatedNow;
    sendEmail(
      user,
      'trial-result',
      `${remediatedNow} pages remediated — ${remaining} pages remaining`,
      renderTrialResultEmail(user, doc, remediatedNow, remaining)
    );
  } else if (done.length > 0) {
    sendEmail(user, 'remediation-complete', 'Your remediated PDFs are ready', renderCompletionEmail(user, done));
  }
}

router.get('/jobs/:id', requireAuth, (req: AuthedRequest, res) => {
  const job = db.jobs.find((j) => j.id === req.params.id && j.userId === req.user!.id);
  if (!job) {
    res.status(404).json({ error: 'Job not found.' });
    return;
  }
  const documents = job.documentIds
    .map((id) => db.documents.find((d) => d.id === id))
    .filter(Boolean);
  res.json({ job, documents, user: publicUser(req.user!) });
});

router.get('/emails', requireAuth, (req: AuthedRequest, res) => {
  res.json({ emails: db.emails.filter((e) => e.userId === req.user!.id) });
});

export default router;
