const express = require('express');
const { paginate, parsePaginationParams } = require('../src/utilities/pagination');
const { ValidationError } = require('../src/utilities/errors');
const { ok, successMessage } = require('../src/utils/responses');

/**
 * Updates route factory
 * @param {Object} deps - Explicit dependencies
 * @param {Object} deps.updateManager - Container update manager
 * @param {Object} deps.selfUpdater - DashCaddy self-update manager
 * @param {Function} deps.asyncHandler - Async route handler wrapper
 * @param {Function} deps.logError - Error logging function
 * @param {Function} deps.ok - Success response helper
 * @returns {express.Router}
 */
module.exports = function({ updateManager, selfUpdater, asyncHandler, logError, ok }) {
  const router = express.Router();

  // ===== UPDATE MANAGEMENT ENDPOINTS =====

  // Check for updates
  router.post('/updates/check', asyncHandler(async (req, res) => {
    await updateManager.checkForUpdates();
    const updates = updateManager.getAvailableUpdates();
    ok(res, { updates, count: updates.length });
  }, 'updates-check'));

  // Get available updates
  router.get('/updates/available', asyncHandler(async (req, res) => {
    const updates = updateManager.getAvailableUpdates();
    const paginationParams = parsePaginationParams(req.query);
    const result = paginate(updates, paginationParams);
    ok(res, { updates: result.data, count: updates.length, ...(result.pagination && { pagination: result.pagination }) });
  }, 'updates-available'));

  // Update a container
  router.post('/updates/update/:containerId', asyncHandler(async (req, res) => {
    const result = await updateManager.updateContainer(req.params.containerId, req.body);
    ok(res, { result });
  }, 'updates-update'));

  // Rollback update
  router.post('/updates/rollback/:containerId', asyncHandler(async (req, res) => {
    await updateManager.rollbackUpdate(req.params.containerId);
    successMessage(res, 'Rollback completed');
  }, 'updates-rollback'));

  // Get update history
  router.get('/updates/history', asyncHandler(async (req, res) => {
    const paginationParams = parsePaginationParams(req.query);
    // When paginating, fetch all history so pagination can slice correctly
    const fetchLimit = paginationParams ? Number.MAX_SAFE_INTEGER : (parseInt(req.query.limit) || 50);
    const history = updateManager.getHistory(fetchLimit);
    const result = paginate(history, paginationParams);
    ok(res, { history: result.data, ...(result.pagination && { pagination: result.pagination }) });
  }, 'updates-history'));

  // Configure auto-update
  router.post('/updates/auto-update/:containerId', asyncHandler(async (req, res) => {
    updateManager.configureAutoUpdate(req.params.containerId, req.body);
    successMessage(res, 'Auto-update configured');
  }, 'updates-auto-update'));

  // Get auto-update configuration
  router.get('/updates/auto-update', asyncHandler(async (req, res) => {
    const config = updateManager.getAutoUpdateConfig();
    ok(res, { config });
  }, 'updates-auto-update-config'));

  // Schedule update
  router.post('/updates/schedule/:containerId', asyncHandler(async (req, res) => {
    const { scheduledTime } = req.body;
    if (!scheduledTime) {
      throw new ValidationError('scheduledTime is required');
    }
    updateManager.scheduleUpdate(req.params.containerId, scheduledTime);
    ok(res, { message: 'Update scheduled', scheduledTime });
  }, 'updates-schedule'));

  // ===== DASHCADDY SELF-UPDATE ENDPOINTS =====

  // Get current version
  router.get('/system/version', asyncHandler(async (req, res) => {
    const local = selfUpdater.getLocalVersion();
    ok(res, { name: 'DashCaddy', version: local.version, commit: local.commit });
  }, 'system-version'));

  // Check for DashCaddy update
  router.get('/system/update-check', asyncHandler(async (req, res) => {
    const result = await selfUpdater.checkForUpdate();
    ok(res, result);
  }, 'system-update-check'));

  // Apply available update
  router.post('/system/update-apply', asyncHandler(async (req, res) => {
    const check = await selfUpdater.checkForUpdate();
    if (!check.available) {
      return successMessage(res, 'Already up to date');
    }
    // Refuse same-version applies. The check.available flag can theoretically be
    // true with equal versions (commit-mismatch path); applying anyway just
    // rebuilds the container without changing anything user-visible and pollutes
    // history with v1.4.0 → v1.4.0 entries.
    const localV = check.local && check.local.version;
    const remoteV = check.remote && check.remote.version;
    if (localV && remoteV && localV === remoteV) {
      return ok(res, { message: 'Already up to date', version: localV });
    }
    // Start async — container may restart
    selfUpdater.applyUpdate(check.remote).catch(err => {
      logError('self-update', err);
    });
    ok(res, {
      message: 'Update initiated',
      fromVersion: localV,
      toVersion: remoteV,
    });
  }, 'system-update-apply'));

  // Notify endpoint — the publishing host POSTs here when a new release is
  // out so the instance can update within seconds instead of waiting for the
  // next 30-min poll. Auth is a shared secret in X-DashCaddy-Notify-Secret
  // (per-instance, generated on first start, lives at
  // <updatesDir>/notify-secret). This route is in the public-routes allowlist
  // because TOTP would block machine-to-machine notifies.
  router.post('/system/update-notify', asyncHandler(async (req, res) => {
    const presented = req.get('X-DashCaddy-Notify-Secret') || '';
    const expected = selfUpdater.getNotifySecret() || '';
    // constant-time compare to avoid timing leaks
    const presentedBuf = Buffer.from(presented);
    const expectedBuf = Buffer.from(expected);
    const secretOk = presentedBuf.length === expectedBuf.length &&
      presentedBuf.length > 0 &&
      require('crypto').timingSafeEqual(presentedBuf, expectedBuf);
    if (!secretOk) {
      return res.status(401).json({ success: false, error: 'Invalid notify secret' });
    }
    const result = selfUpdater.notifyAndApply('http-notify');
    ok(res, result);
  }, 'system-update-notify'));

  // Get update status
  router.get('/system/update-status', asyncHandler(async (req, res) => {
    ok(res, {
      status: selfUpdater.getStatus(),
      lastCheck: selfUpdater.lastCheckTime,
      lastResult: selfUpdater.lastCheckResult,
    });
  }, 'system-update-status'));

  // Get self-update history
  router.get('/system/update-history', asyncHandler(async (req, res) => {
    const history = selfUpdater.getUpdateHistory();
    ok(res, { history });
  }, 'system-update-history'));

  // List rollback versions
  router.get('/system/rollback-versions', asyncHandler(async (req, res) => {
    const versions = selfUpdater.getAvailableRollbacks();
    ok(res, { versions });
  }, 'system-rollback-versions'));

  // Rollback to a previous version
  router.post('/system/rollback', asyncHandler(async (req, res) => {
    const { version } = req.body;
    if (!version) throw new ValidationError('version is required');
    selfUpdater.rollbackToVersion(version).catch(err => {
      logError('self-rollback', err);
    });
    ok(res, { message: `Rollback to ${version} initiated` });
  }, 'system-rollback'));

  return router;
};
