/**
 * routes/takedownCheckNow.js
 *
 * Superadmin-only "Check now" button. The link health worker runs
 * every 4h, but sometimes the operator (me, right now) just sent a
 * takedown or just got off the phone with a platform rep and wants
 * to know "is the page still up?" RIGHT NOW. This route runs the
 * same probe + classification pipeline that the worker uses, but
 * synchronously, on a single takedown.
 *
 * Costs 1 Scrape.do credit. Capped at 6 calls/minute per user to
 * prevent abuse. SUPERADMIN ONLY — the audit trail matters and the
 * Scrape.do credit burn adds up. Returns the same shape as the
 * worker's applyResult outcome.
 */

'use strict';

const express = require('express');
const router  = express.Router();
const { requireLogin } = require('../middleware/auth');
const Takedown = require('../models/takedown');
const Case    = require('../models/case');
const linkHealth = require('../jobs/linkHealthWorker');
const linkHealthRepo = require('../models/linkHealth');
const Audit   = require('../models/auditLog');
const alertBus = require('../services/alertBus');
const { db } = require('../config/db');
const contactsRepo = require('../services/platformContactsRepo');
const rateLimit = (() => {
  // tiny in-memory rate limiter (6/min per user). Resets on process
  // restart, which is fine for a single-tenant install.
  const buckets = new Map();
  return (req, res, next) => {
    const key = req.user.id + ':' + Math.floor(Date.now() / 60000);
    const cur = buckets.get(key) || 0;
    if (cur >= 6) return res.status(429).json({ error: 'RATE_LIMIT', message: '6 checks per minute max' });
    buckets.set(key, cur + 1);
    next();
  };
})();

router.post('/takedown/:id/check-now', requireLogin, rateLimit, async (req, res) => {
  // SUPERADMIN ONLY. Operator owner used to be allowed; tightened
  // because every check burns a Scrape.do credit and the operator
  // needs an audit trail of who manually probed what.
  if (req.user.role !== 'superadmin') {
    return res.status(403).json({ error: 'FORBIDDEN', message: 'Manual health check is superadmin-only.' });
  }
  const id = parseInt(req.params.id, 10);
  const td = Takedown.getById(id);
  if (!td) return res.status(404).json({ error: 'NOT_FOUND' });
  try {
    const c = Case.getById(td.case_id);
    const prev = linkHealthRepo.latestFor(td.id);
    const checker = require('../services/linkHealthChecker');
    const check = await checker.checkUrl(td.url, { victimName: c ? c.victim_name : null, previousBodySize: prev ? prev.body_size : null });
    // Apply — this also marks the takedown as 'removed' on success
    const outcome = await linkHealth.applyResult(td, check);
    // Audit log + alert so we know who manually probed what. Manual
    // checks are rare; getting it into the alert feed helps spot
    // "the operator is doing 30 checks in 5 minutes" patterns.
    try {
      Audit.log(req.user, 'takedown.manual_health_check', `takedown:${td.id}`, {
        url: td.url, domain: td.domain,
        check_status: check.status, http_status: check.http_status,
        outcome, reason: check.reason,
      });
    } catch (_) { /* best-effort */ }
    if (check.status === 'removed' || check.status === 'soft_404') {
      try {
        alertBus.raise({
          severity: 'info', category: 'takedown',
          title: `Manual probe confirmed removal: ${td.domain}`,
          detail: { takedown_id: td.id, check_status: check.status, by: req.user.email },
          ref_url: `takedown:${td.id}`, dedupe_key: `manual_probe_removed:${td.id}`,
        });
      } catch (_) { /* best-effort */ }
    }
    res.json({ ok: true, check, outcome, takedown: Takedown.getById(id) });
  } catch (e) {
    res.status(500).json({ error: 'CHECK_FAILED', message: e.message });
  }
});

// --- Bulk actions ---

// POST /takedown/bulk { ids: [...], action: 'mark_removed' | 'mark_responded' | 'mark_failed' }
// Updates all listed takedowns in one shot. Used by the dashboard
// "mark all removed" / "mark all responded" / "mark all failed" buttons.
router.post('/takedown/bulk', requireLogin, async (req, res) => {
  const { ids, action } = req.body || {};
  if (!Array.isArray(ids) || !ids.length) return res.status(400).json({ error: 'ids_required' });
  if (ids.length > 200) return res.status(400).json({ error: 'too_many_ids', message: '200 max per request' });
  const actionMap = {
    mark_removed:    { status: 'removed',    set_resolved: true  },
    mark_responded:  { status: 'responded',  set_resolved: true  },
    mark_failed:     { status: 'failed',     set_resolved: false },
    requeue:         { status: 'production_pending', set_resolved: false, set_next: Date.now() + 30 * 60 * 60 * 1000 },
  };
  if (!actionMap[action]) return res.status(400).json({ error: 'unknown_action', allowed: Object.keys(actionMap) });
  const cfg = actionMap[action];
  const { db } = require('../config/db');
  let updated = 0, skipped = 0, errors = 0;
  for (const id of ids) {
    const td = Takedown.getById(id);
    if (!td) { skipped++; continue; }
    if (req.user.role !== 'superadmin' && td.user_id !== req.user.id) { skipped++; continue; }
    try {
      const sets = ['status = ?', 'updated_at = ?'];
      const vals = [cfg.status, Date.now()];
      if (cfg.set_resolved) { sets.push('resolved_at = ?'); vals.push(Date.now()); }
      if (cfg.set_next) { sets.push('next_escalate_at = ?'); vals.push(cfg.set_next); }
      vals.push(id);
      db.prepare(`UPDATE takedowns SET ${sets.join(', ')} WHERE id = ?`).run(...vals);
      if ((cfg.status === 'removed' || cfg.status === 'responded') && td.domain && td.contact_email) {
        contactsRepo.recordObservedSuccess({ domain: td.domain, contact: td.contact_email });
      }
      if (cfg.status === 'failed' && td.domain && td.contact_email) {
        contactsRepo.recordFailure({ domain: td.domain, contact: td.contact_email, reason: 'bulk-action' });
      }
      // Record the audit event
      const Audit = require('../models/auditLog');
      Audit.log(req.user, 'takedown.bulk_' + action, `takedown:${id}`, { via: 'bulk-action' });
      updated++;
    } catch (_) { errors++; }
  }
  res.json({ ok: true, action, updated, skipped, errors, total: ids.length });
});

// GET /takedown/export.csv — full takedown list as CSV. Operator
// can open in Excel, filter, share with lawyers.
router.get('/takedown/export.csv', requireLogin, async (req, res) => {
  const isSuper = req.user.role === 'superadmin';
  const rows = isSuper
    ? db.prepare('SELECT * FROM takedowns ORDER BY created_at DESC LIMIT 5000').all()
    : db.prepare('SELECT * FROM takedowns WHERE user_id = ? ORDER BY created_at DESC LIMIT 5000').all(req.user.id);
  // CSV columns
  const cols = ['id','case_id','user_id','url','domain','contact_email','status','sent_at','escalated_at','resolved_at','next_escalate_at','created_at'];
  const header = cols.join(',');
  const esc = (s) => {
    if (s == null) return '';
    const v = String(s);
    return /[",\n]/.test(v) ? '"' + v.replace(/"/g, '""') + '"' : v;
  };
  const csv = [header].concat(rows.map(r => cols.map(c => esc(r[c])).join(','))).join('\n');
  res.setHeader('Content-Type', 'text/csv; charset=utf-8');
  res.setHeader('Content-Disposition', `attachment; filename="ncii-takedowns-${new Date().toISOString().slice(0,10)}.csv"`);
  res.send(csv);
});

module.exports = router;
