/**
 * Dependencies Route — REST API for service dependency tracking
 *
 * Endpoints:
 *  GET    /dependencies/graph                Full dependency graph
 *  GET    /dependencies/validate             Validate a proposed dep chain
 *  GET    /dependencies/:serviceId           Direct deps for one service
 *  GET    /dependencies/:serviceId/chain     Ordered restart chain
 *  GET    /dependencies/:serviceId/status    Dependency health status
 *  POST   /dependencies/:serviceId           Set dependencies
 *  DELETE /dependencies/:serviceId           Remove all dependencies
 *  POST   /dependencies/:serviceId/restart   Restart with dependency chain
 *
 * @module routes/dependencies
 */

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

/**
 * Dependencies route factory
 *
 * @param {Object} deps - Explicit dependencies
 * @param {Object} deps.dependencyManager - DependencyManager instance
 * @param {Object} deps.servicesStateManager - State manager for services.json
 * @param {Object} deps.docker - Docker client wrapper
 * @param {Function} deps.asyncHandler - Async route handler wrapper
 * @param {Function} deps.logError - Error logging function
 * @param {Function} deps.resyncHealthChecker - Health checker resync function
 * @param {Object} deps.log - Logger instance
 * @returns {express.Router}
 */
module.exports = function({
  dependencyManager,
  servicesStateManager,
  docker,
  asyncHandler,
  logError,
  resyncHealthChecker,
  log,
}) {
  const router = express.Router();

  // -------------------------------------------------------------------------
  // GET /dependencies/graph — Full dependency graph
  // -------------------------------------------------------------------------
  router.get('/graph', asyncHandler(async (req, res) => {
    const graph = await dependencyManager.getDependencyGraph();
    success(res, { graph });
  }, 'dep-graph'));

  // -------------------------------------------------------------------------
  // GET /dependencies/validate — Validate a proposed dep chain (query params)
  // -------------------------------------------------------------------------
  router.get('/validate', asyncHandler(async (req, res) => {
    const { serviceId, dependsOn } = req.query;

    if (!serviceId) {
      throw new ValidationError('serviceId query parameter is required');
    }

    // dependsOn may be a comma-separated string or already an array
    let parsed;
    if (Array.isArray(dependsOn)) {
      parsed = dependsOn;
    } else if (typeof dependsOn === 'string' && dependsOn.length > 0) {
      parsed = dependsOn.split(',').map(s => s.trim()).filter(Boolean);
    } else {
      parsed = [];
    }

    const result = await dependencyManager.validateDependencies(serviceId, parsed);
    success(res, result);
  }, 'dep-validate'));

  // -------------------------------------------------------------------------
  // GET /dependencies/:serviceId — Direct deps for one service
  // -------------------------------------------------------------------------
  router.get('/:serviceId', asyncHandler(async (req, res) => {
    const { serviceId } = req.params;
    const dependencies = await dependencyManager.getDependencies(serviceId);
    const dependents = await dependencyManager.getDependents(serviceId);

    // Read the service's current dependsOn array
    const services = await servicesStateManager.read();
    const allServices = Array.isArray(services) ? services : (services.services || []);
    const service = allServices.find(s => s.id === serviceId);

    if (!service) {
      throw new NotFoundError(`Service "${serviceId}"`);
    }

    success(res, {
      serviceId,
      dependsOn: service.dependsOn || [],
      dependencies,
      dependents: dependents.map(d => ({ id: d.id, name: d.name })),
    });
  }, 'dep-get'));

  // -------------------------------------------------------------------------
  // GET /dependencies/:serviceId/chain — Ordered restart chain
  // -------------------------------------------------------------------------
  router.get('/:serviceId/chain', asyncHandler(async (req, res) => {
    const { serviceId } = req.params;
    const chain = await dependencyManager.getOrderedRestartChain(serviceId);
    success(res, { serviceId, chain });
  }, 'dep-chain'));

  // -------------------------------------------------------------------------
  // GET /dependencies/:serviceId/status — Dependency health status
  // -------------------------------------------------------------------------
  router.get('/:serviceId/status', asyncHandler(async (req, res) => {
    const { serviceId } = req.params;
    const statuses = await dependencyManager.getDependencyStatus(serviceId);
    success(res, { serviceId, statuses });
  }, 'dep-status'));

  // -------------------------------------------------------------------------
  // POST /dependencies/:serviceId — Set dependencies
  // -------------------------------------------------------------------------
  router.post('/:serviceId', asyncHandler(async (req, res) => {
    const { serviceId } = req.params;
    const { dependsOn } = req.body;

    if (!Array.isArray(dependsOn)) {
      throw new ValidationError('Request body must include dependsOn as an array of service IDs');
    }

    // Validate first
    const validation = await dependencyManager.validateDependencies(serviceId, dependsOn);
    if (!validation.valid) {
      return errorResponse(res, validation.errors.join('; '), 400);
    }

    // Update the service
    let found = false;
    await servicesStateManager.update(services => {
      const arr = Array.isArray(services) ? services : [];
      return arr.map(s => {
        if (s.id === serviceId) {
          found = true;
          return { ...s, dependsOn: dependsOn.slice() };
        }
        return s;
      });
    });

    if (!found) {
      throw new NotFoundError(`Service "${serviceId}"`);
    }

    log.info('dependency', 'Dependencies updated', { serviceId, dependsOn });

    success(res, {
      message: `Dependencies updated for "${serviceId}"`,
      serviceId,
      dependsOn,
    });
  }, 'dep-set'));

  // -------------------------------------------------------------------------
  // DELETE /dependencies/:serviceId — Remove all dependencies for a service
  // -------------------------------------------------------------------------
  router.delete('/:serviceId', asyncHandler(async (req, res) => {
    const { serviceId } = req.params;

    let found = false;
    await servicesStateManager.update(services => {
      const arr = Array.isArray(services) ? services : [];
      return arr.map(s => {
        if (s.id === serviceId) {
          found = true;
          const updated = { ...s };
          delete updated.dependsOn;
          return updated;
        }
        return s;
      });
    });

    if (!found) {
      throw new NotFoundError(`Service "${serviceId}"`);
    }

    log.info('dependency', 'Dependencies removed', { serviceId });

    success(res, {
      message: `All dependencies removed for "${serviceId}"`,
      serviceId,
    });
  }, 'dep-delete'));

  // -------------------------------------------------------------------------
  // POST /dependencies/:serviceId/restart — Restart with dependency chain
  // -------------------------------------------------------------------------
  router.post('/:serviceId/restart', asyncHandler(async (req, res) => {
    const { serviceId } = req.params;

    // Verify the service exists
    const services = await servicesStateManager.read();
    const allServices = Array.isArray(services) ? services : (services.services || []);
    if (!allServices.find(s => s.id === serviceId)) {
      throw new NotFoundError(`Service "${serviceId}"`);
    }

    // Get the chain first for the response (before async restart begins)
    let chain;
    try {
      chain = await dependencyManager.getOrderedRestartChain(serviceId);
    } catch (err) {
      return errorResponse(res, err.message, 400);
    }

    // Respond immediately with the chain order
    success(res, {
      message: `Dependency restart initiated for "${serviceId}"`,
      serviceId,
      chain,
    });

    // Run the restart chain asynchronously so the client doesn't block
    dependencyManager.restartWithDependencies(serviceId).catch(err => {
      if (log) {
        log.error('dependency', 'Async dependency restart failed', {
          serviceId,
          error: err.message,
        });
      }
    });
  }, 'dep-restart'));

  return router;
};
