const express = require('express');
const { exists } = require('../../fs-helpers');
const { REGEX } = require('../../constants');

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

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

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

  // Check port availability
  router.get('/apps/ports/:port/check', ctx.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('/apps/ports/:basePort/suggest', ctx.asyncHandler(async (req, res) => {
    const basePort = parseInt(req.params.basePort) || 8080;
    const maxAttempts = 100;
    const usedPorts = await ctx.docker.getUsedPorts();
    for (let port = basePort; port < basePort + maxAttempts; port++) {
      if (!usedPorts.has(port)) {
        res.json({ success: true, suggestedPort: port, basePort });
        return;
      }
    }
    ctx.errorResponse(res, 400, `No available ports found in range ${basePort}-${basePort + maxAttempts}`);
  }, 'suggest-port'));

  // Update subdomain for deployed app
  router.post('/apps/update-subdomain', ctx.asyncHandler(async (req, res) => {
    const { serviceId, oldSubdomain, newSubdomain, containerId, ip } = req.body;
    if (!oldSubdomain || typeof oldSubdomain !== 'string') {
      return ctx.errorResponse(res, 400, 'oldSubdomain is required');
    }
    if (!newSubdomain || typeof newSubdomain !== 'string') {
      return ctx.errorResponse(res, 400, 'newSubdomain is required');
    }
    if (!REGEX.SUBDOMAIN.test(newSubdomain)) {
      return ctx.errorResponse(res, 400, '[DC-301] Invalid subdomain format for newSubdomain');
    }
    ctx.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);
        const result = await ctx.dns.call(ctx.siteConfig.dnsServerIp, '/api/zones/records/delete', {
          token: ctx.dns.getToken(), domain: oldDomain, type: 'A', ipAddress: ip || 'localhost'
        });
        results.oldDns = result.status === 'ok' ? 'deleted' : result.errorMessage;
        ctx.log.info('dns', 'Old DNS record deleted', { domain: oldDomain });
      } catch (error) {
        results.oldDns = `failed: ${error.message}`;
        ctx.log.warn('dns', 'Old DNS deletion warning', { error: error.message });
      }
    }

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

    try {
      if (await exists(ctx.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 ctx.caddy.read();
        if (oldBlockRegex.test(content)) {
          const caddyResult = await ctx.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}`;
      ctx.log.error('caddy', 'Caddy update error', { error: error.message });
    }

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

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

  return router;
};
