const express = require('express');
const { DOCKER } = require('../src/utilities/constants');
const { paginate, parsePaginationParams } = require('../src/utilities/pagination');
const { NotFoundError } = require('../src/utilities/errors');
const { success } = require('../src/utils/responses');

/**
 * Containers route factory
 * @param {Object} deps - Explicit dependencies
 * @param {Object} deps.docker - Docker client wrapper (client, pull methods)
 * @param {Object} deps.log - Logger instance
 * @param {Function} deps.asyncHandler - Async route handler wrapper
 * @param {Object} deps.workflowEngine - WorkflowEngine instance (optional)
 * @returns {express.Router}
 */
module.exports = function({ docker, log, asyncHandler, workflowEngine }) {
  const router = express.Router();

  // Helper: verify container exists before operating on it
  async function getVerifiedContainer(id) {
    const container = docker.client.getContainer(id);
    try {
      await container.inspect();
    } catch (err) {
      if (err.statusCode === 404 || (err.message && err.message.includes('no such container'))) {
        throw new NotFoundError(`Container ${id}`);
      }
      throw err;
    }
    return container;
  }

  // Start container
  router.post('/:id/start', asyncHandler(async (req, res) => {
    const container = await getVerifiedContainer(req.params.id);
    await container.start();
    success(res, { message: 'Container started' });
  }, 'container-start'));

  // Stop container
  router.post('/:id/stop', asyncHandler(async (req, res) => {
    const container = await getVerifiedContainer(req.params.id);
    await container.stop();
    success(res, { message: 'Container stopped' });
  }, 'container-stop'));

  // Restart container
  router.post('/:id/restart', asyncHandler(async (req, res) => {
    const container = await getVerifiedContainer(req.params.id);
    await container.restart();
    success(res, { message: 'Container restarted' });
  }, 'container-restart'));

  // Update container to latest image version
  router.post('/:id/update', asyncHandler(async (req, res) => {
    const containerId = req.params.id;
    const container = await getVerifiedContainer(containerId);

      // Get container info
      const containerInfo = await container.inspect();
      const imageName = containerInfo.Config.Image;
      const containerName = containerInfo.Name.replace(/^\//, '');

      log.info('docker', 'Updating container', { containerName, imageName });

      // Pull the latest image
      log.info('docker', `Pulling latest image: ${imageName}`);
      await docker.pull(imageName);

      // Trigger pre-update workflow (backup before update)
      if (workflowEngine) {
        try { await workflowEngine.triggerEvent('pre-update', { containerId: containerId, containerName, imageName }); } catch (w) { log.warn('workflow', 'pre-update trigger failed: ' + w.message); }
      }

      // Get current container config for recreation
      const hostConfig = containerInfo.HostConfig;
      const config = {
        Image: imageName,
        name: containerName,
        Env: containerInfo.Config.Env,
        ExposedPorts: containerInfo.Config.ExposedPorts,
        Labels: containerInfo.Config.Labels,
        HostConfig: {
          Binds: hostConfig.Binds,
          PortBindings: hostConfig.PortBindings,
          RestartPolicy: hostConfig.RestartPolicy,
          NetworkMode: hostConfig.NetworkMode,
          ExtraHosts: hostConfig.ExtraHosts,
          Privileged: hostConfig.Privileged,
          CapAdd: hostConfig.CapAdd,
          CapDrop: hostConfig.CapDrop,
          Devices: hostConfig.Devices,
          LogConfig: DOCKER.LOG_CONFIG  // Ensure log rotation on updated containers
        },
        NetworkingConfig: {}
      };

      // Get network settings if using a custom network
      if (hostConfig.NetworkMode && !['bridge', 'host', 'none'].includes(hostConfig.NetworkMode)) {
        const networkName = hostConfig.NetworkMode;
        config.NetworkingConfig.EndpointsConfig = {
          [networkName]: containerInfo.NetworkSettings.Networks[networkName]
        };
      }

      // Stop and remove old container
      log.info('docker', 'Stopping container', { containerName });
      await container.stop().catch(() => {}); // Ignore if already stopped
      log.info('docker', 'Removing container', { containerName });
      await container.remove();

      // Wait for port release (Windows/Docker Desktop can be slow to free ports)
      await new Promise(r => setTimeout(r, 3000));

      // Create and start new container
      log.info('docker', 'Creating new container', { containerName });
      let newContainer;
      try {
        newContainer = await docker.client.createContainer(config);
        log.info('docker', 'Starting container', { containerName });
        await newContainer.start();
      } catch (startError) {
        // Clean up the failed container so it doesn't block future attempts
        log.error('docker', 'Failed to start new container', { containerName, error: startError.message });
        if (newContainer) {
          try { await newContainer.remove({ force: true }); } catch (e) { /* already gone */ }
        }
        throw startError;
      }

      const newContainerInfo = await newContainer.inspect();

      // Prune dangling images after update
      try {
        const pruneResult = await docker.client.pruneImages({ filters: { dangling: { true: true } } });
        if (pruneResult.SpaceReclaimed > 0) {
          log.info('docker', 'Pruned dangling images after update', { spaceReclaimed: Math.round(pruneResult.SpaceReclaimed / 1024 / 1024) + 'MB' });
        }
      } catch (pruneErr) {
        log.debug('docker', 'Image prune after update failed', { error: pruneErr.message });
      }

      success(res, {
              message: `Container ${containerName} updated successfully`,
              newContainerId: newContainerInfo.Id
            });

            // Trigger post-update workflow
            if (workflowEngine) {
              try { await workflowEngine.triggerEvent('post-update', { containerId: containerId, containerName, imageName, newContainerId: newContainerInfo.Id }); } catch (w) { log.warn('workflow', 'post-update trigger failed: ' + w.message); }
            }
          }, 'container-update'));

  // Check for available updates (compares local and remote image digests)
  router.get('/:id/check-update', asyncHandler(async (req, res) => {
    const containerId = req.params.id;
    const container = await getVerifiedContainer(containerId);
    const containerInfo = await container.inspect();
    const imageName = containerInfo.Config.Image;

    const localImage = docker.client.getImage(containerInfo.Image);
    const localImageInfo = await localImage.inspect();
    const localDigest = localImageInfo.RepoDigests?.[0] || null;

    let updateAvailable = false;
    try {
      const pullStream = await docker.pull(imageName);

      const downloadedLayers = pullStream.filter(e =>
        e.status === 'Downloading' || e.status === 'Download complete'
      );
      updateAvailable = downloadedLayers.length > 0;

      const newImage = docker.client.getImage(imageName);
      const newImageInfo = await newImage.inspect();
      const newDigest = newImageInfo.RepoDigests?.[0] || null;

      if (localDigest && newDigest && localDigest !== newDigest) {
        updateAvailable = true;
      }
    } catch (pullError) {
      log.debug('docker', 'Could not check for updates', { error: pullError.message });
    }

    success(res, {
      imageName,
      updateAvailable,
      currentDigest: localDigest
    });
  }, 'container-check-update'));

  // Get container logs
  router.get('/:id/logs', asyncHandler(async (req, res) => {
    const container = await getVerifiedContainer(req.params.id);
    const logs = await container.logs({
      stdout: true,
      stderr: true,
      tail: 100,
      timestamps: true
    });
    success(res, { logs: logs.toString() });
  }, 'container-logs'));

  // Update resource limits on a running container
  router.put('/:id/resources', asyncHandler(async (req, res) => {
    const container = await getVerifiedContainer(req.params.id);
    const { memory, cpus } = req.body;
    const updateConfig = {};

    if (memory !== undefined) {
      updateConfig.Memory = memory > 0 ? Math.round(memory * 1024 * 1024) : 0; // MB to bytes, 0 = unlimited
      updateConfig.MemoryReservation = memory > 0 ? Math.round(memory * 1024 * 1024 * 0.5) : 0;
    }
    if (cpus !== undefined) {
      updateConfig.NanoCpus = cpus > 0 ? Math.round(cpus * 1e9) : 0; // 0 = unlimited
    }

    await container.update(updateConfig);
    success(res, { message: 'Resource limits updated' });
  }, 'container-resources'));

  // Get resource limits for a container
  router.get('/:id/resources', asyncHandler(async (req, res) => {
    const container = await getVerifiedContainer(req.params.id);
    const info = await container.inspect();
    const hc = info.HostConfig;
    success(res, {
      memory: hc.Memory ? Math.round(hc.Memory / 1024 / 1024) : 0, // bytes to MB
      memoryReservation: hc.MemoryReservation ? Math.round(hc.MemoryReservation / 1024 / 1024) : 0,
      cpus: hc.NanoCpus ? hc.NanoCpus / 1e9 : 0,
    });
  }, 'container-resources-get'));

  // Delete container
  router.delete('/:id', asyncHandler(async (req, res) => {
    const container = await getVerifiedContainer(req.params.id);
    await container.remove({ force: true });
    success(res, { message: 'Container removed' });
  }, 'container-delete'));

  // Discover running containers
  router.get('/discover', asyncHandler(async (req, res) => {
    const containers = await docker.client.listContainers({ all: true });
    const samiContainers = containers.filter(container =>
      container.Labels && container.Labels['sami.managed'] === 'true'
    );

    const discoveredContainers = samiContainers.map(container => ({
      id: container.Id,
      name: container.Names[0].replace('/', ''),
      image: container.Image,
      state: container.State,
      status: container.Status,
      appTemplate: container.Labels['sami.app'],
      subdomain: container.Labels['sami.subdomain'],
      ports: container.Ports
    }));

    const paginationParams = parsePaginationParams(req.query);
    const result = paginate(discoveredContainers, paginationParams);
    success(res, { containers: result.data, ...(result.pagination && { pagination: result.pagination }) });
  }, 'containers-discover'));

  return router;
};
