const express = require('express');
const { success, error: errorResponse } = require('../src/utils/responses');
const { ValidationError } = require('../src/utilities/errors');

/**
 * Disk space management routes
 *
 * GET  /disk                 — current usage snapshot (budget, breakdown, status)
 * GET  /disk/breakdown       — detailed breakdown incl. per-container log sizes
 * GET  /disk/config          — get disk budget settings
 * POST /disk/config          — update disk budget settings
 * POST /disk/cleanup         — trigger manual cleanup (standard|aggressive|logs-only)
 */

// DC-059: monotonic-ordering invariant for the three threshold percentages.
// DiskSpaceMonitor._getBudgetStatus() walks them in order
// (cleanupAggressivePct → criticalThresholdPct → warningThresholdPct) and
// returns at the FIRST threshold the usage crosses. If a caller writes
// them out of order (e.g. warningThresholdPct=95, criticalThresholdPct=60),
// the higher-priority branches become unreachable and the monitor silently
// misclassifies budget state. Validate against the *effective* config
// (current value + incoming update for each field) so partial updates can
// be applied one field at a time without violating the invariant.
//
// Clamp values to the same ranges the previous inline Math.min/Math.max
// chains enforced (warning 50..99, critical 60..99, aggressive 70..99)
// so we don't loosen the original bounds while adding the new check.
const THRESHOLD_BOUNDS = Object.freeze({
  warning:    { min: 50, max: 99 },
  critical:   { min: 60, max: 99 },
  aggressive: { min: 70, max: 99 },
});

function clampThreshold(name, value) {
  const { min, max } = THRESHOLD_BOUNDS[name];
  return Math.min(Math.max(value, min), max);
}

/**
 * Apply a candidate update to a baseline config, then verify the three
 * threshold percentages still satisfy
 *   warningThresholdPct < criticalThresholdPct < cleanupAggressivePct.
 * The POST /config endpoint accepts partial updates (single field at a
 * time), so we merge into the live diskSpaceMonitor config first, then test
 * the merged value. Returns the merged candidate on success; throws
 * ValidationError if the ordering invariant would be violated.
 *
 * @param {Object} baseline - current effective config from diskSpaceMonitor
 * @param {Object} candidate - the partial update being applied this request
 * @returns {Object} merged candidate with thresholds clamped to bounds
 */
function mergeAndCheckOrdering(baseline, candidate) {
  const next = { ...baseline };
  if (typeof candidate.warningThresholdPct === 'number') {
    next.warningThresholdPct = clampThreshold('warning', candidate.warningThresholdPct);
  }
  if (typeof candidate.criticalThresholdPct === 'number') {
    next.criticalThresholdPct = clampThreshold('critical', candidate.criticalThresholdPct);
  }
  if (typeof candidate.cleanupAggressivePct === 'number') {
    next.cleanupAggressivePct = clampThreshold('aggressive', candidate.cleanupAggressivePct);
  }
  if (!(next.warningThresholdPct < next.criticalThresholdPct)) {
    throw new ValidationError(
      `warningThresholdPct (${next.warningThresholdPct}) must be strictly less than criticalThresholdPct (${next.criticalThresholdPct})`,
      'warningThresholdPct'
    );
  }
  if (!(next.criticalThresholdPct < next.cleanupAggressivePct)) {
    throw new ValidationError(
      `criticalThresholdPct (${next.criticalThresholdPct}) must be strictly less than cleanupAggressivePct (${next.cleanupAggressivePct})`,
      'criticalThresholdPct'
    );
  }
  // Return only the fields the caller asked to change (preserves partial-
  // update semantics; diskSpaceMonitor.configure does its own merge).
  const out = {};
  if (typeof candidate.warningThresholdPct === 'number') out.warningThresholdPct = next.warningThresholdPct;
  if (typeof candidate.criticalThresholdPct === 'number') out.criticalThresholdPct = next.criticalThresholdPct;
  if (typeof candidate.cleanupAggressivePct === 'number') out.cleanupAggressivePct = next.cleanupAggressivePct;
  return out;
}

module.exports = function({ diskSpaceMonitor, asyncHandler, log }) {
  const router = express.Router();

  // Current disk usage snapshot
  router.get('/', asyncHandler(async (req, res) => {
    const snapshot = await diskSpaceMonitor.getSnapshot();
    success(res, snapshot);
  }, 'disk-get'));

  // Detailed breakdown (includes per-container log sizes)
  router.get('/breakdown', asyncHandler(async (req, res) => {
    const breakdown = await diskSpaceMonitor.getDetailedBreakdown();
    success(res, breakdown);
  }, 'disk-breakdown'));

  // Get disk budget config
  router.get('/config', asyncHandler(async (req, res) => {
    success(res, diskSpaceMonitor.getConfig());
  }, 'disk-config-get'));

  // Update disk budget config
  router.post('/config', asyncHandler(async (req, res) => {
    const { diskBudgetGB, warningThresholdPct, criticalThresholdPct, autoCleanup, enabled, cleanupAggressivePct } = req.body;

    const updates = {};
    if (typeof diskBudgetGB === 'number' && diskBudgetGB > 0) updates.diskBudgetGB = Math.min(diskBudgetGB, 1000);
    // DC-059: threshold percentages must satisfy a strict monotonic order
    // (warning < critical < aggressive) so _getBudgetStatus() reaches the
    // correct branch. mergeAndCheckOrdering() validates against the live
    // baseline, so partial updates that violate the invariant are rejected
    // BEFORE we mutate diskSpaceMonitor.diskConfig.
    const thresholdUpdates = mergeAndCheckOrdering(
      diskSpaceMonitor.getConfig(),
      { warningThresholdPct, criticalThresholdPct, cleanupAggressivePct }
    );
    Object.assign(updates, thresholdUpdates);
    if (typeof autoCleanup === 'boolean') updates.autoCleanup = autoCleanup;
    if (typeof enabled === 'boolean') updates.enabled = enabled;

    const config = diskSpaceMonitor.configure(updates);
    log.info('disk', 'Disk budget updated', updates);

    success(res, { message: 'Disk budget updated', config });
  }, 'disk-config-set'));

  // Manual cleanup trigger
  router.post('/cleanup', asyncHandler(async (req, res) => {
    const level = req.body?.level || 'standard';
    if (!['standard', 'aggressive', 'logs-only'].includes(level)) {
      return errorResponse(res, 'Invalid cleanup level. Use: standard, aggressive, or logs-only', 400);
    }

    log.info('disk', 'Manual cleanup triggered', { level, by: req.auth?.user || 'api' });
    const result = await diskSpaceMonitor.performCleanup(level);
    success(res, result);
  }, 'disk-cleanup'));

  return router;
};
