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

module.exports = function(ctx) {
  const router = express.Router();

  // Helper: verify container exists before operating on it
  async function getVerifiedContainer(id) {
    const container = ctx.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', ctx.asyncHandler(async (req, res) => {
    const container = await getVerifiedContainer(req.params.id);
    await container.start();
    res.json({ success: true, message: 'Container started' });
  }, 'container-start'));

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

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

  // Update container to latest image version
  router.post('/:id/update', ctx.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(/^\//, '');

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

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

      // 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
      ctx.log.info('docker', 'Stopping container', { containerName });
      await container.stop().catch(() => {}); // Ignore if already stopped
      ctx.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
      ctx.log.info('docker', 'Creating new container', { containerName });
      let newContainer;
      try {
        newContainer = await ctx.docker.client.createContainer(config);
        ctx.log.info('docker', 'Starting container', { containerName });
        await newContainer.start();
      } catch (startError) {
        // Clean up the failed container so it doesn't block future attempts
        ctx.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 ctx.docker.client.pruneImages({ filters: { dangling: { true: true } } });
        if (pruneResult.SpaceReclaimed > 0) {
          ctx.log.info('docker', 'Pruned dangling images after update', { spaceReclaimed: Math.round(pruneResult.SpaceReclaimed / 1024 / 1024) + 'MB' });
        }
      } catch (pruneErr) {
        ctx.log.debug('docker', 'Image prune after update failed', { error: pruneErr.message });
      }

      res.json({
        success: true,
        message: `Container ${containerName} updated successfully`,
        newContainerId: newContainerInfo.Id
      });
  }, 'container-update'));

  // Check for available updates (compares local and remote image digests)
  router.get('/:id/check-update', ctx.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 = ctx.docker.client.getImage(containerInfo.Image);
    const localImageInfo = await localImage.inspect();
    const localDigest = localImageInfo.RepoDigests?.[0] || null;

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

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

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

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

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

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

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

  // Discover running containers
  router.get('/discover', ctx.asyncHandler(async (req, res) => {
    const containers = await ctx.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);
    res.json({ success: true, containers: result.data, ...(result.pagination && { pagination: result.pagination }) });
  }, 'containers-discover'));

  return router;
};
