/**
 * Super Admin — Backup Logs Routes
 *
 * Dedicated write/audit endpoints for the Super Admin "Backup Logs" module.
 * This module READS the existing Backup collection through the already
 * super-admin-aware endpoints (GET /api/backup/list, GET /api/backup/:id) — it
 * does NOT duplicate backup records or backup business logic.
 *
 * These endpoints exist because the corresponding routes on /api/backup require
 * an active company context (a pure Super Admin has none), so this file provides
 * super-admin-only equivalents for the small set of write actions plus an audit
 * sink for read actions (view / download / export / print).
 *
 * The existing Backup & Restore module, its routes, model, engine and scheduler
 * are NOT modified by this file.
 */

import express, { Request, Response } from 'express';
import fs from 'fs';
import crypto from 'crypto';
import mongoose from 'mongoose';
import { authenticate, requireRole } from '../middleware/auth';
import { getModels } from '../models';
import { logAudit } from '../utils/auditLogger';

const router = express.Router();

router.use(authenticate);
router.use(requireRole('super-admin'));

/**
 * POST /verify-download
 * Password-verify the current Super Admin, then issue a one-time download token
 * on the backup (5-minute expiry). The existing token-based GET
 * /api/backup/:id/download stream then consumes it — that stream is not company
 * gated, so it already works for a Super Admin; only THIS token issuance needed a
 * company-free variant.
 */
router.post('/verify-download', async (req: Request, res: Response) => {
  try {
    const { backupId, password } = req.body || {};
    if (!backupId || !password) {
      res.status(400).json({ error: 'Backup ID and password are required' });
      return;
    }
    if (!mongoose.isValidObjectId(backupId)) {
      res.status(400).json({ error: 'Invalid backup id' });
      return;
    }

    const user: any = req.user!;
    const isMatch = typeof user.comparePassword === 'function' ? await user.comparePassword(password) : false;
    if (!isMatch) {
      res.status(401).json({ error: 'Invalid credentials. Please try again.' });
      return;
    }

    const { Backup } = getModels();
    const backup = await Backup.findById(backupId);
    if (!backup) {
      res.status(404).json({ error: 'Backup not found' });
      return;
    }
    if (backup.status !== 'completed') {
      res.status(400).json({ error: 'Only completed backups can be downloaded' });
      return;
    }

    const downloadToken = crypto.randomBytes(32).toString('hex');
    const downloadTokenExpires = new Date(Date.now() + 5 * 60 * 1000);
    await Backup.findByIdAndUpdate(backupId, { downloadToken, downloadTokenExpires });

    await logAudit({
      userId: String(user._id || user.id),
      userEmail: user.email,
      action: 'backup-log.download',
      resource: 'Backup',
      resourceId: String(backupId),
      companyId: backup.companyId || 'platform',
      details: { backupName: backup.name },
      req,
    });

    res.json({ data: { downloadToken, downloadUrl: `/backup/${backupId}/download?token=${downloadToken}` } });
  } catch (err: any) {
    console.error('[BackupLogs] verify-download error:', err?.message);
    res.status(500).json({ error: err?.message || 'Failed to verify credentials' });
  }
});

/** Actions the frontend may record for read-only interactions. */
const AUDIT_ACTIONS = new Set(['view', 'download', 'export', 'print']);

/**
 * POST /audit
 * Record a read-only Backup Logs interaction (view / download / export / print)
 * for the audit trail. Never mutates any backup.
 */
router.post('/audit', async (req: Request, res: Response) => {
  try {
    const action = String(req.body.action || '').toLowerCase();
    if (!AUDIT_ACTIONS.has(action)) {
      res.status(400).json({ error: 'Invalid audit action' });
      return;
    }
    await logAudit({
      userId: String(req.user!._id || (req.user as any).id),
      userEmail: req.user!.email,
      action: `backup-log.${action}`,
      resource: 'Backup',
      resourceId: req.body.backupId ? String(req.body.backupId) : undefined,
      companyId: 'platform',
      details: req.body.details || (req.body.format ? { format: req.body.format, count: req.body.count } : undefined),
      req,
    });
    res.json({ success: true });
  } catch (err: any) {
    console.error('[BackupLogs] Audit error:', err?.message);
    res.status(500).json({ error: 'Failed to record audit event' });
  }
});

/**
 * PATCH /:id
 * Edit a backup's permitted metadata (name). Super Admin only. Does not touch
 * the stored backup archive.
 */
router.patch('/:id', async (req: Request, res: Response) => {
  const { id } = req.params;
  if (!mongoose.isValidObjectId(id)) {
    res.status(400).json({ error: `Invalid backup id: "${id}".` });
    return;
  }
  try {
    const { Backup } = getModels();
    const backup = await Backup.findById(id);
    if (!backup) {
      res.status(404).json({ error: 'Backup not found' });
      return;
    }

    const name = typeof req.body.name === 'string' ? req.body.name.trim() : undefined;
    if (name !== undefined) {
      if (!name) {
        res.status(400).json({ error: 'Backup name cannot be empty' });
        return;
      }
      if (name.length > 200) {
        res.status(400).json({ error: 'Backup name cannot exceed 200 characters' });
        return;
      }
      backup.name = name;
    }

    await backup.save();

    await logAudit({
      userId: String(req.user!._id || (req.user as any).id),
      userEmail: req.user!.email,
      action: 'backup-log.edit',
      resource: 'Backup',
      resourceId: id,
      companyId: backup.companyId || 'platform',
      details: { name: backup.name },
      req,
    });

    res.json({ data: backup.toObject() });
  } catch (err: any) {
    console.error('[BackupLogs] Edit error:', err?.message);
    res.status(500).json({ error: err?.message || 'Failed to update backup' });
  }
});

/**
 * DELETE /:id
 * Delete a backup (archive on disk + database record). Super Admin only.
 */
router.delete('/:id', async (req: Request, res: Response) => {
  const { id } = req.params;
  if (!mongoose.isValidObjectId(id)) {
    res.status(400).json({ error: `Invalid backup id: "${id}".` });
    return;
  }
  try {
    const { Backup } = getModels();
    const backup = await Backup.findById(id);
    if (!backup) {
      res.status(404).json({ error: 'Backup not found' });
      return;
    }

    // Record first, then the file. Unlinking first meant an interruption in
    // between (fs.unlinkSync throws EBUSY/EPERM on Windows when the ZIP is held
    // open, and the process can die at the await) left a completed record
    // pointing at a file that was already gone — which cleanupOrphanedFiles
    // then correctly rewrites to Failed "file not found", breaking restore.
    // Reversed, the same interruption leaves a harmless orphan file instead.
    // Matches the ordering in routes/backupRestore.ts and the worker's own
    // deleteBackup().
    await Backup.findByIdAndDelete(id);
    if (backup.filePath && fs.existsSync(backup.filePath)) {
      try { fs.unlinkSync(backup.filePath); } catch { /* best-effort */ }
    }

    await logAudit({
      userId: String(req.user!._id || (req.user as any).id),
      userEmail: req.user!.email,
      action: 'backup-log.delete',
      resource: 'Backup',
      resourceId: id,
      companyId: backup.companyId || 'platform',
      details: { backupName: backup.name },
      req,
    });

    res.json({ data: { message: 'Backup deleted successfully', id } });
  } catch (err: any) {
    console.error('[BackupLogs] Delete error:', err?.message);
    res.status(500).json({ error: err?.message || 'Failed to delete backup' });
  }
});

/**
 * POST /bulk-delete
 * Delete many backups at once. Super Admin only.
 */
router.post('/bulk-delete', async (req: Request, res: Response) => {
  try {
    const ids: string[] = Array.isArray(req.body.ids) ? req.body.ids : [];
    if (ids.length === 0) {
      res.status(400).json({ error: 'ids must be a non-empty array' });
      return;
    }
    const validIds = ids.filter((i) => mongoose.isValidObjectId(i));
    if (validIds.length === 0) {
      res.status(400).json({ error: 'No valid backup ids provided' });
      return;
    }

    const { Backup } = getModels();
    const backups = await Backup.find({ _id: { $in: validIds } });

    let deleted = 0;
    for (const backup of backups) {
      // Record first, then the file — see the single-delete note above.
      await Backup.findByIdAndDelete(backup._id);
      if (backup.filePath && fs.existsSync(backup.filePath)) {
        try { fs.unlinkSync(backup.filePath); } catch { /* best-effort */ }
      }
      deleted += 1;
    }

    await logAudit({
      userId: String(req.user!._id || (req.user as any).id),
      userEmail: req.user!.email,
      action: 'backup-log.bulk-delete',
      resource: 'Backup',
      companyId: 'platform',
      details: { requested: ids.length, deleted },
      req,
    });

    res.json({ data: { message: `Deleted ${deleted} backup(s)`, deleted } });
  } catch (err: any) {
    console.error('[BackupLogs] Bulk delete error:', err?.message);
    res.status(500).json({ error: err?.message || 'Failed to delete backups' });
  }
});

export default router;
