const express = require('express');
const { exists } = require('../../src/utilities/fs-helpers');
/**
 * Apps templates routes factory
 * @param {Object} deps - Explicit dependencies
 * @param {Object} deps.servicesStateManager - Services state manager
 * @param {Function} deps.asyncHandler - Async route handler wrapper
 * @param {Object} deps.helpers - Apps helpers module
 * @param {Object} deps.APP_TEMPLATES - App templates registry
 * @param {Object} deps.TEMPLATE_CATEGORIES - Template categories
 * @param {Object} deps.DIFFICULTY_LEVELS - Difficulty levels
 * @param {Object} deps.docker - Docker client
 * @param {Object} deps.caddy - Caddy client
 * @param {Object} deps.dns - DNS context
 * @param {Object} deps.siteConfig - Site configuration
 * @param {Function} deps.buildDomain - Build domain helper
 * @param {Function} deps.errorResponse - Error response helper
 * @param {Object} deps.log - Logger instance
 * @param {string} deps.SERVICES_FILE - Services file path
 * @returns {express.Router}
 */
const { REGEX } = require('../../src/utilities/constants');
const { ok } = require('../../src/utils/responses');

module.exports = function({
  servicesStateManager, asyncHandler, helpers,
  APP_TEMPLATES, TEMPLATE_CATEGORIES, DIFFICULTY_LEVELS,
  docker, caddy, dns, siteConfig, buildDomain,
  errorResponse, log, SERVICES_FILE
}) {
  const router = express.Router();

  // Ctx shim for backward compatibility with existing route code
  const ctx = {
    APP_TEMPLATES,
    TEMPLATE_CATEGORIES,
    DIFFICULTY_LEVELS,
    dns,
    siteConfig,
    buildDomain,
    SERVICES_FILE
  };

  // Get available app templates
  router.get('/templates', asyncHandler(async (req, res) => {
    ok(res, {
      templates: ctx.APP_TEMPLATES,
      categories: ctx.TEMPLATE_CATEGORIES,
      difficultyLevels: ctx.DIFFICULTY_LEVELS
    });
  }, 'apps-templates'));

  // Get specific app template
  router.get('/templates/:appId', asyncHandler(async (req, res) => {
    const { appId } = req.params;
    const template = ctx.APP_TEMPLATES[appId];
    if (!template) {
      const { NotFoundError } = require('../../src/utilities/errors');
      throw new NotFoundError('App template');
    }
    ok(res, { template });
  }, 'apps-template-detail'));

  // Check port availability
  router.get('/ports/:port/check', asyncHandler(async (req, res) => {
    const port = req.params.port;
    const conflicts = await helpers.checkPortConflicts([port]);
    if (conflicts.length > 0) {
      const conflict = conflicts[0];
      res.json({ available: false, port, conflict: { usedBy: conflict.usedBy, app: conflict.app, containerId: conflict.containerId } });
    } else {
      res.json({ available: true, port });
    }
  }, 'check-port'));

  // Get suggested available port
  router.get('/ports/:basePort/suggest', asyncHandler(async (req, res) => {
    const basePort = parseInt(req.params.basePort) || 8080;
    const maxAttempts = 100;
    const usedPorts = await docker.getUsedPorts();
    for (let port = basePort; port < basePort + maxAttempts; port++) {
      if (!usedPorts.has(port)) {
        ok(res, { suggestedPort: port, basePort });
        return;
      }
    }
    errorResponse(res, 400, `No available ports found in range ${basePort}-${basePort + maxAttempts}`);
  }, 'suggest-port'));

  // Update subdomain for deployed app
  router.post('/update-subdomain', asyncHandler(async (req, res) => {
    const { serviceId, oldSubdomain, newSubdomain, containerId, ip } = req.body;
    const { ValidationError } = require('../../src/utilities/errors');

    if (!oldSubdomain || typeof oldSubdomain !== 'string') {
      throw new ValidationError('oldSubdomain is required');
    }
    if (!newSubdomain || typeof newSubdomain !== 'string') {
      throw new ValidationError('newSubdomain is required');
    }
    if (!REGEX.SUBDOMAIN.test(newSubdomain)) {
      throw new ValidationError('[DC-301] Invalid subdomain format for newSubdomain');
    }
    log.info('deploy', 'Updating subdomain', { oldSubdomain, newSubdomain });
    const results = { oldDns: null, newDns: null, caddy: null, service: null };

    if (oldSubdomain && ctx.dns.getToken()) {
      try {
        const oldDomain = oldSubdomain.includes('.') ? oldSubdomain : ctx.buildDomain(oldSubdomain);
        await ctx.dns.universalDeleteRecord(oldDomain, ip || 'localhost');
        results.oldDns = 'deleted';
        log.info('dns', 'Old DNS record deleted', { domain: oldDomain });
      } catch (error) {
        results.oldDns = `failed: ${error.message}`;
        log.warn('dns', 'Old DNS deletion warning', { error: error.message });
      }
    }

    if (newSubdomain && ctx.dns.getToken()) {
      try {
        await ctx.dns.universalCreateRecord(newSubdomain, ip || 'localhost');
        results.newDns = 'created';
        log.info('dns', 'New DNS record created', { domain: ctx.buildDomain(newSubdomain) });
      } catch (error) {
        results.newDns = `failed: ${error.message}`;
        log.warn('dns', 'New DNS creation warning', { error: error.message });
      }
    }

    try {
      if (await exists(caddy.filePath)) {
        const oldDomain = oldSubdomain.includes('.') ? oldSubdomain : ctx.buildDomain(oldSubdomain);
        const newDomain = newSubdomain.includes('.') ? newSubdomain : ctx.buildDomain(newSubdomain);
        const escapedOld = oldDomain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const oldBlockRegex = new RegExp(`${escapedOld}(?::\\d+)?\\s*\\{[^{}]*(?:\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\}[^{}]*)*\\}`, 'g');
        const content = await caddy.read();
        if (oldBlockRegex.test(content)) {
          const caddyResult = await caddy.modify(c => {
            const re = new RegExp(`${escapedOld}(?::\\d+)?\\s*\\{[^{}]*(?:\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\}[^{}]*)*\\}`, 'g');
            return c.replace(re, match => match.replace(oldDomain, newDomain));
          });
          results.caddy = caddyResult.success ? 'updated' : 'updated (reload failed)';
        } else {
          results.caddy = 'old config not found';
        }
      } else {
        results.caddy = 'caddyfile not found';
      }
    } catch (error) {
      results.caddy = `failed: ${error.message}`;
      log.error('caddy', 'Caddy update error', { error: error.message });
    }

    try {
      if (await exists(ctx.SERVICES_FILE)) {
        await servicesStateManager.update(services => {
          const serviceIndex = services.findIndex(s => s.id === oldSubdomain || s.id === serviceId);
          if (serviceIndex !== -1) {
            services[serviceIndex].id = newSubdomain;
            results.service = 'updated';
            log.info('deploy', 'Service config updated in services.json');
          } else {
            results.service = 'not found';
          }
          return services;
        });
      }
    } catch (error) {
      results.service = `failed: ${error.message}`;
      log.warn('deploy', 'Service update warning', { error: error.message || String(error) });
    }

    ok(res, {
      message: `Subdomain updated: ${oldSubdomain} -> ${newSubdomain}`,
      newUrl: `https://${ctx.buildDomain(newSubdomain)}`,
      results
    });
  }, 'update-subdomain'));

  return router;
};
