const express = require('express');
const fs = require('fs');
const fsp = require('fs').promises;
const validatorLib = require('validator');
const { APP, TIMEOUTS, CADDY, DNS_RECORD_TYPES, REGEX, SESSION_TTL } = require('../src/utilities/constants');
const { exists } = require('../src/utilities/fs-helpers');
const { success, error: errorResponse } = require('../src/utils/responses');
const { ValidationError, AuthenticationError, NotFoundError } = require('../src/utilities/errors');

/**
 * DNS routes factory
 * @param {Object} deps - Explicit dependencies
 * @param {Object} deps.dns - DNS management interface (call, requireToken, getToken, etc.)
 * @param {Object} deps.siteConfig - Site configuration
 * @param {Function} deps.asyncHandler - Async route handler wrapper
 * @param {Object} deps.log - Logger instance
 * @param {Function} deps.safeErrorMessage - Safe error message extractor
 * @param {Function} deps.fetchT - Fetch wrapper with timeout
 * @param {Object} deps.credentialManager - Credential storage manager
 * @returns {express.Router}
 */
module.exports = function({
  dns,
  siteConfig,
  asyncHandler,
  log,
  safeErrorMessage,
  fetchT,
  credentialManager,
  dnsPropagationChecker
}) {
  const router = express.Router();

  /** Validate that a server IP is in the configured DNS servers list */
  function validateDnsServer(server) {
    const serverIp = server.includes(':') ? server.split(':')[0] : server;
    if (!validatorLib.isIP(serverIp)) return null;
    const configuredIps = Object.values(siteConfig.dnsServers || {}).map(s => s.ip).filter(Boolean);
    // Also allow the default dnsServerIp
    if (siteConfig.dnsServerIp) configuredIps.push(siteConfig.dnsServerIp);
    if (!configuredIps.includes(serverIp)) return null;
    return serverIp;
  }

  // ===== DNS PROVIDER ENDPOINTS =====

  // GET /providers — List all available DNS providers
  router.get('/providers', asyncHandler(async (req, res) => {
    const providers = dns.getAvailableProviders ? dns.getAvailableProviders() : [];
    const activeProvider = dns.getProviderId ? dns.getProviderId() : 'technitium';
    success(res, { providers, activeProvider });
  }, 'dns-providers-list'));

  // GET /provider/status — Get active provider status
  router.get('/provider/status', asyncHandler(async (req, res) => {
    if (!dns.getActiveProvider) {
      return success(res, { providerId: 'technitium', capabilities: ['create-record', 'delete-record', 'resolve', 'list-records', 'logs', 'restart', 'update-check', 'credentials', 'zones'] });
    }
    try {
      const provider = dns.getActiveProvider();
      const status = await provider.getStatus();
      success(res, status);
    } catch (err) {
      errorResponse(res, safeErrorMessage(err), 500);
    }
  }, 'dns-provider-status'));

  // ===== UNIVERSAL RECORD ENDPOINTS (work with any provider) =====

  // POST /universal/record — Create a DNS record via any provider
  router.post('/universal/record', asyncHandler(async (req, res) => {
    if (!dns.getActiveProvider) {
      // Fallback to legacy Technitium route
      return res.redirect(307, '/api/dns/record');
    }
    const { domain, ip, ttl, type, server } = req.body;
    if (!domain || !ip) throw new ValidationError('domain and ip are required');
    if (!REGEX.DOMAIN.test(domain)) throw new ValidationError('[DC-301] Invalid domain format');
    if (!validatorLib.isIP(ip)) throw new ValidationError('[DC-210] Invalid IP address');

    try {
      const provider = dns.getActiveProvider();
      if (!provider.supportsCapability('create-record')) {
        const result = await provider.createRecord({
          domain, zone: siteConfig.tld?.replace(/^\./, '') || '',
          type: type || 'A', value: ip, ttl: ttl || 300, overwrite: true
        });
        return success(res, {
          message: result.message || `DNS record instructions provided`,
          manual: true,
          instructions: result.instructions
        });
      }

      const result = await provider.createRecord({
        domain, zone: siteConfig.tld?.replace(/^\./, '') || '',
        type: type || 'A', value: ip, ttl: ttl || 300, overwrite: true
      });

      // Start propagation check in background
      if (dnsPropagationChecker && ip) {
        dnsPropagationChecker.startVerification(domain, ip).catch(err => {
          log('DNS propagation check start failed:', err.message);
        });
      }

      success(res, {
        message: result.status === 'manual' ? result.message : `DNS record ${domain} -> ${ip} created`,
        provider: dns.getProviderId(),
        ...(result.instructions ? { manual: true, instructions: result.instructions } : {})
      });
    } catch (error) {
      log.error('dns', 'Universal DNS record creation error', { error: error.message });
      errorResponse(res, safeErrorMessage(error), 500);
    }
  }, 'dns-universal-create'));

  // DELETE /universal/record — Delete a DNS record via any provider
  router.delete('/universal/record', asyncHandler(async (req, res) => {
    if (!dns.getActiveProvider) {
      return res.redirect(307, '/api/dns/record');
    }
    const { domain, type, value } = req.query;
    if (!domain) throw new ValidationError('domain is required');
    if (!REGEX.DOMAIN.test(domain)) throw new ValidationError('[DC-301] Invalid domain format');

    try {
      const provider = dns.getActiveProvider();
      const result = await provider.deleteRecord({
        domain, type: type || 'A', value
      });

      success(res, {
        message: result.status === 'manual' ? result.message : `DNS record ${domain} deleted`,
        provider: dns.getProviderId(),
        ...(result.instructions ? { manual: true, instructions: result.instructions } : {})
      });
    } catch (error) {
      log.error('dns', 'Universal DNS record deletion error', { error: error.message });
      errorResponse(res, safeErrorMessage(error), 500);
    }
  }, 'dns-universal-delete'));

  // GET /universal/resolve — Resolve a domain via any provider
  router.get('/universal/resolve', asyncHandler(async (req, res) => {
    if (!dns.getActiveProvider) {
      return res.redirect(307, '/api/dns/resolve');
    }
    const { domain, type } = req.query;
    if (!domain) throw new ValidationError('domain is required');
    if (!REGEX.DOMAIN.test(domain)) throw new ValidationError('[DC-301] Invalid domain format');

    try {
      const provider = dns.getActiveProvider();
      const result = await provider.resolveRecords({
        domain, zone: siteConfig.tld?.replace(/^\./, '') || '',
        type: type || 'A'
      });

      if (result.response?.records?.length > 0) {
        const ipAddresses = result.response.records
          .filter(r => r.type === (type || 'A'))
          .map(r => r.rData?.ipAddress || r.content || r.rData?.address)
          .filter(Boolean);
        success(res, { answer: ipAddresses });
      } else {
        throw new NotFoundError('No records found for domain');
      }
    } catch (error) {
      log.error('dns', 'Universal DNS resolve error', { error: error.message });
      errorResponse(res, safeErrorMessage(error), error.statusCode || 500);
    }
  }, 'dns-universal-resolve'));

  // ===== LEGACY TECHNITIUM-SPECIFIC ROUTES (unchanged) =====
  router.delete('/record', asyncHandler(async (req, res) => {
    const { domain, type, token, server, ipAddress } = req.query;

    const dnsToken = await dns.requireToken(token);

    if (!domain) {
      throw new ValidationError('domain is required');
    }

    // Validate domain format
    if (!REGEX.DOMAIN.test(domain)) {
      throw new ValidationError('[DC-301] Invalid domain format');
    }

    // Validate record type
    if (type && !DNS_RECORD_TYPES.includes(type.toUpperCase())) {
      throw new ValidationError('Invalid DNS record type');
    }

    // Validate ipAddress if provided
    if (ipAddress && !validatorLib.isIP(ipAddress)) {
      throw new ValidationError('[DC-210] Invalid IP address');
    }

    // Validate server against configured DNS servers
    if (server && !validateDnsServer(server)) {
      throw new ValidationError('Server must be a configured DNS server');
    }

    // Default to dns1 LAN IP, allow override
    const dnsServer = server || siteConfig.dnsServerIp;
    const recordType = type || 'A';

    try {
      const p = { token: dnsToken, domain: domain, type: recordType };
      if (ipAddress) p.ipAddress = ipAddress;
      const result = await dns.call(dnsServer, '/api/zones/records/delete', p);

      if (result.status === 'ok') {
        success(res, { message: `DNS record ${domain} deleted` });
      } else {
        // Error handled by middleware
      }
    } catch (error) {
      // Error handled by middleware
    }
  }, 'dns-delete-record'));

  // POST /record — Create a DNS record in Technitium
  router.post('/record', asyncHandler(async (req, res) => {
    const { domain, ip, ttl, token, server } = req.body;

    const dnsToken = await dns.requireToken(token);

    if (!domain || !ip) {
      throw new ValidationError('domain and ip are required');
    }

    // Validate domain format
    if (!REGEX.DOMAIN.test(domain)) {
      throw new ValidationError('[DC-301] Invalid domain format');
    }

    // Validate IP address
    if (!validatorLib.isIP(ip)) {
      throw new ValidationError('[DC-210] Invalid IP address');
    }

    // Validate TTL if provided
    if (ttl !== undefined) {
      const parsedTtl = parseInt(ttl, 10);
      if (isNaN(parsedTtl) || parsedTtl < CADDY.TTL_MIN || parsedTtl > CADDY.TTL_MAX) {
        return errorResponse(res, `TTL must be between ${CADDY.TTL_MIN} and ${CADDY.TTL_MAX}`, 400);
      }
    }

    // Validate server against configured DNS servers
    if (server && !validateDnsServer(server)) {
      throw new ValidationError('Server must be a configured DNS server');
    }

    // Default to dns1 LAN IP since Docker container can't access Tailscale network
    const dnsServer = server || siteConfig.dnsServerIp;
    const recordTtl = ttl || 300;

    try {
      // For Technitium, we need zone and subdomain separated
      // domain = "test.sami" -> zone = "sami", subdomain = "test"
      const parts = domain.split('.');
      const subdomain = parts[0];
      const zone = parts.slice(1).join('.') || siteConfig.tld.replace(/^\./, '');

      const result = await dns.call(dnsServer, '/api/zones/records/add', {
        token: dnsToken, domain, zone, type: 'A', ipAddress: ip, ttl: recordTtl.toString(), overwrite: 'true'
      });

      if (result.status === 'ok') {
        // Start DNS propagation verification in background
        if (dnsPropagationChecker && ip) {
          const fullDomain = domain;
          dnsPropagationChecker.startVerification(fullDomain, ip).catch(err => {
            log('DNS propagation check start failed:', err.message);
          });
        }

        success(res, { message: `DNS record ${domain} -> ${ip} created` });
      } else {
        // Error handled by middleware
      }
    } catch (error) {
      log.error('dns', 'DNS record creation error', { error: error.message });
      errorResponse(res, safeErrorMessage(error), 500, { details: error.cause?.code || 'fetch failed' });
    }
  }, 'dns-create-record'));

  // GET /resolve — Resolve a domain to IP address via Technitium
  router.get('/resolve', asyncHandler(async (req, res) => {
    const { domain, server, token } = req.query;

    const dnsToken = await dns.requireToken(token);

    if (!domain) {
      throw new ValidationError('domain is required');
    }

    // Validate domain format
    if (!REGEX.DOMAIN.test(domain)) {
      throw new ValidationError('[DC-301] Invalid domain format');
    }

    // Validate server against configured DNS servers
    if (server && !validateDnsServer(server)) {
      throw new ValidationError('Server must be a configured DNS server');
    }

    const dnsServer = server || siteConfig.dnsServerIp;

    try {
      const result = await dns.call(dnsServer, '/api/zones/records/get', {
        token: dnsToken, domain, zone: siteConfig.tld.replace(/^\./, ''), listZone: 'true'
      });

      if (result.status === 'ok' && result.response && result.response.records) {
        // Find A records for this domain
        const aRecords = result.response.records.filter(r => r.type === 'A');
        if (aRecords.length > 0) {
          const ipAddresses = aRecords.map(r => r.rData?.ipAddress).filter(Boolean);
          success(res, { answer: ipAddresses });
        } else {
          throw new NotFoundError('No A records found for domain');
        }
      } else {
        // Error handled by middleware
      }
    } catch (error) {
      log.error('dns', 'DNS resolve error', { error: error.message });
      // Error handled by middleware
    }
  }, 'dns-resolve'));

  // GET /logs — Fetch DNS query logs (Technitium only)
  router.get('/logs', asyncHandler(async (req, res) => {
    // Capability gate: logs are provider-specific
    if (dns.supportsCapability && !dns.supportsCapability('logs')) {
      return success(res, { server: 'N/A', count: 0, logs: [], message: 'DNS logs not supported by current provider' });
    }

    const { server, limit } = req.query;

    if (!server) {
      throw new ValidationError('server is required');
    }

    // Validate server against configured DNS servers
    const serverIp = validateDnsServer(server);
    if (!serverIp) {
      throw new ValidationError('Server must be a configured DNS server');
    }

    const logLimit = Math.min(parseInt(limit) || 25, 1000);
    const dnsPort = siteConfig.dnsServerPort || '5380';

    try {
      // Auto-authenticate using stored read-only credentials for log access
      const authResult = await dns.getTokenForServer(serverIp, 'readonly');
      if (!authResult.success) {
        throw new AuthenticationError('DNS auto-authentication failed. Ensure credentials are configured via the DNS panel.');
      }
      const effectiveToken = authResult.token;

      // Try to get available log files first
      const listUrl = `http://${serverIp}:${dnsPort}/api/logs/list?token=${encodeURIComponent(effectiveToken)}`;
      const listResponse = await fetchT(listUrl, { method: 'GET', headers: { 'Accept': 'application/json' } });

      let logFileName = new Date().toISOString().split('T')[0]; // Default to today

      if (listResponse.ok) {
        const listResult = await listResponse.json();
        if (listResult.status === 'ok' && listResult.response?.logFiles?.length > 0) {
          // Use most recent log file
          logFileName = listResult.response.logFiles[0].fileName;
        }
      }

      // Technitium logs/download endpoint - returns plain text logs
      const technitiumUrl = `http://${serverIp}:${dnsPort}/api/logs/download?token=${encodeURIComponent(effectiveToken)}&fileName=${logFileName}`;
      log.info('dns', 'Fetching DNS logs', { server, logFileName });

      const response = await fetchT(technitiumUrl, {
        method: 'GET',
        headers: { 'Accept': 'text/plain' }
      }, 10000);

      if (!response.ok) {
        const errorText = await response.text();
        // Try to parse error as JSON
        try {
          const errorJson = JSON.parse(errorText);
          if (errorJson.errorMessage?.includes('Could not find file')) {
            return success(res, {
              server: server,
              count: 0,
              logs: [],
              message: 'No logs available for this server'
            });
          }
          return errorResponse(res, safeErrorMessage(errorJson.errorMessage || errorText), response.status);
        } catch {
          return errorResponse(res, 'DNS server returned an error', response.status);
        }
      }

      // Parse plain text logs
      const logText = await response.text();

      // Check if it's an error JSON response
      if (logText.startsWith('{')) {
        try {
          const errorJson = JSON.parse(logText);
          if (errorJson.status && errorJson.status !== 'ok') {
            if (errorJson.errorMessage?.includes('Could not find file')) {
              return success(res, {
                server: server,
                count: 0,
                logs: [],
                message: 'No logs available for this server'
              });
            }
            // Invalidate cached token on auth errors so next request re-authenticates
            if (errorJson.status === 'invalid-token') {
              dns.invalidateTokenForServer(serverIp);
            }
            return errorResponse(res, safeErrorMessage(errorJson.errorMessage), 400);
          }
        } catch { /* Not JSON, continue parsing as text */ }
      }

      const allLines = logText.split('\n').filter(line => line.trim() && !line.includes('Logging started'));

      // Get last N lines (most recent)
      const recentLines = allLines.slice(-logLimit);

      // Parse each log line into structured format
      const parsedLogs = recentLines.map(line => {
        // Format: [2026-01-24 04:17:43 Local] [47.147.82.245:60001] [UDP] QNAME: domain; QTYPE: A; QCLASS: IN; RCODE: Refused; ANSWER: []
        const match = line.match(/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})[^\]]*\]\s*\[([^\]]+)\]\s*\[(\w+)\]\s*QNAME:\s*([^;]+);\s*QTYPE:\s*([^;]+);\s*QCLASS:\s*([^;]+);\s*RCODE:\s*([^;]+);\s*ANSWER:\s*\[([^\]]*)\]/);

        if (match) {
          return {
            timestamp: match[1],
            client: match[2].split(':')[0], // Remove port
            protocol: match[3],
            domain: match[4].trim(),
            type: match[5].trim(),
            class: match[6].trim(),
            rcode: match[7].trim(),
            answer: match[8].trim() || null,
            raw: line
          };
        }
        return { raw: line, parsed: false };
      }).reverse(); // Reverse to show most recent first

      log.info('dns', 'Returning DNS log entries', { count: parsedLogs.length, logFileName });
      success(res, {
        server: server,
        logFile: logFileName,
        count: parsedLogs.length,
        logs: parsedLogs
      });

    } catch (error) {
      log.error('dns', 'DNS logs proxy error', { error: error.message });
      // Error handled by middleware
    }
  }, 'dns-logs'));

  // GET /token-status — Check DNS token/credentials status
  router.get('/token-status', asyncHandler(async (req, res) => {
    const username = await credentialManager.retrieve('dns.username');
    const hasCredentials = !!username || await exists(dns.credentialsFile);
    const hasToken = !!dns.getToken();

    success(res, {
      hasCredentials,
      hasToken,
      tokenExpiry: dns.getTokenExpiry(),
      isExpired: dns.getTokenExpiry() ? new Date() > new Date(dns.getTokenExpiry()) : null
    });
  }, 'dns-token-status'));

  // POST /credentials — Store DNS credentials (encrypted)
  // Accepts per-server format: { servers: { dns1: { username, password }, dns2: {...}, dns3: {...} } }
  // Also accepts legacy format: { username, password, server }
  router.post('/credentials', asyncHandler(async (req, res) => {
    const { servers, username, password, server } = req.body;
    const dangerousChars = [';', '&', '|', '`', '$', '\n', '\r'];
    const dnsPort = siteConfig.dnsServerPort || '5380';

    // Per-server format: { servers: { dns1: { readonly: { username, password }, admin: { username, password } }, ... } }
    if (servers && typeof servers === 'object') {
      const results = {};
      let anySuccess = false;

      for (const [dnsId, creds] of Object.entries(servers)) {
        // Look up server IP from config
        const serverInfo = siteConfig.dnsServers?.[dnsId];
        const serverIp = serverInfo?.ip;
        if (!serverIp) {
          results[dnsId] = { success: false, error: `No IP configured for ${dnsId}` };
          continue;
        }

        const savedTypes = [];

        // Process both readonly and admin credential types
        for (const credType of ['readonly', 'admin']) {
          const typeCreds = creds[credType];
          if (!typeCreds || !typeCreds.username || !typeCreds.password) continue;

          if (typeCreds.username.length > 100 || typeCreds.password.length > 512) {
            results[dnsId] = { success: false, error: `${credType} credentials exceed maximum length` };
            continue;
          }
          if (dangerousChars.some(char => typeCreds.username.includes(char))) {
            results[dnsId] = { success: false, error: `${credType} username contains invalid characters` };
            continue;
          }

          // Test credentials by logging in to the target server
          try {
            const testResult = await dns.refresh(typeCreds.username, typeCreds.password, serverIp);
            if (testResult.success) {
              await credentialManager.store(`dns.${dnsId}.${credType}.username`, typeCreds.username, { type: 'dns', role: credType, server: serverIp });
              await credentialManager.store(`dns.${dnsId}.${credType}.password`, typeCreds.password, { type: 'dns', role: credType, server: serverIp });
              savedTypes.push(credType);
              anySuccess = true;
              log.info('dns', `${credType} credentials saved for ${dnsId}`, { server: serverIp });
            } else {
              if (!results[dnsId]) {
                results[dnsId] = { success: false, error: `${credType}: ${testResult.error || 'Login failed'}` };
              }
            }
          } catch (err) {
            if (!results[dnsId]) {
              results[dnsId] = { success: false, error: `${credType}: ${err.message}` };
            }
          }
        }

        if (savedTypes.length > 0) {
          if (savedTypes.length === 2) {
            results[dnsId] = { success: true };
          } else {
            results[dnsId] = { success: true, partial: `${savedTypes[0]} verified` };
          }
        }
      }

      return ok(res, {
        message: anySuccess ? 'Credentials saved for one or more servers' : 'All server credential tests failed',
        results
      });
    }

    // Legacy single-credential format: { username, password, server }
    if (!username || !password) {
      throw new ValidationError('username and password are required');
    }

    if (username.length > 100 || password.length > 512) {
      throw new ValidationError('Credentials exceed maximum length');
    }

    if (dangerousChars.some(char => username.includes(char))) {
      throw new ValidationError('Username contains invalid characters');
    }

    if (server && !validateDnsServer(server)) {
      throw new ValidationError('Server must be a configured DNS server');
    }

    const testResult = await dns.refresh(username, password, server || siteConfig.dnsServerIp);

    if (!testResult.success) {
      return errorResponse(res, `Invalid credentials: ${testResult.error}`, 401);
    }

    const dnsServer = server || siteConfig.dnsServerIp;
    await credentialManager.store('dns.username', username, { type: 'dns', server: dnsServer });
    await credentialManager.store('dns.password', password, { type: 'dns', server: dnsServer });
    await credentialManager.store('dns.server', dnsServer, { type: 'dns' });
    log.info('dns', 'DNS credentials saved to credential manager (encrypted)');

    success(res, {
      message: 'DNS credentials saved and verified (encrypted)',
      tokenExpiry: dns.getTokenExpiry()
    });
  }, 'dns-credentials'));

  // DELETE /credentials — Delete stored DNS credentials
  router.delete('/credentials', asyncHandler(async (req, res) => {
    // Delete global credentials
    await credentialManager.delete('dns.username');
    await credentialManager.delete('dns.password');
    await credentialManager.delete('dns.server');
    // Delete per-server credentials (both old flat and new typed format)
    for (const dnsId of Object.keys(siteConfig.dnsServers || {})) {
      await credentialManager.delete(`dns.${dnsId}.username`);
      await credentialManager.delete(`dns.${dnsId}.password`);
      for (const role of ['readonly', 'admin']) {
        await credentialManager.delete(`dns.${dnsId}.${role}.username`);
        await credentialManager.delete(`dns.${dnsId}.${role}.password`);
      }
    }
    if (await exists(dns.credentialsFile)) {
      await fsp.unlink(dns.credentialsFile);
    }
    dns.setToken('');
    dns.setTokenExpiry(null);
    log.info('dns', 'DNS credentials deleted from credential manager');
    success(res, { message: 'DNS credentials removed' });
  }, 'dns-credentials-delete'));

  // POST /restart/:dnsId — Restart a DNS server (Technitium only)
  router.post('/restart/:dnsId', asyncHandler(async (req, res) => {
    // Capability gate
    if (dns.supportsCapability && !dns.supportsCapability('restart')) {
      return errorResponse(res, 'Server restart not supported by current DNS provider', 501);
    }

    const { dnsId } = req.params;
    const serverInfo = siteConfig.dnsServers?.[dnsId];
    if (!serverInfo?.ip) {
      return errorResponse(res, `Unknown DNS server: ${dnsId}`, 400);
    }

    const tokenResult = await dns.getTokenForServer(serverInfo.ip, 'admin');
    if (!tokenResult.success) {
      throw new AuthenticationError('DNS admin authentication failed. Ensure admin credentials are configured.');
    }

    const dnsPort = siteConfig.dnsServerPort || '5380';
    try {
      const url = `http://${serverInfo.ip}:${dnsPort}/api/admin/restart?token=${encodeURIComponent(tokenResult.token)}`;
      const response = await fetchT(url, { method: 'POST' }, 5000);
      const result = await response.json();
      if (result.status === 'ok') {
        success(res, { message: 'Restart initiated' });
      } else {
        // Error handled by middleware
      }
    } catch (err) {
      // Connection drop is expected during restart
      success(res, { message: 'Restart initiated (connection closed)' });
    }
  }, 'dns-restart'));

  // POST /refresh-token — Force refresh DNS token
  router.post('/refresh-token', asyncHandler(async (req, res) => {
    const result = await dns.ensureToken();

    if (result.success) {
      success(res, {
        message: 'Token refreshed successfully',
        tokenExpiry: dns.getTokenExpiry()
      });
    } else {
      errorResponse(res, result.error, 401);
    }
  }, 'dns-refresh-token'));

  // GET /check-update — Check for DNS server updates (Technitium only)
  router.get('/check-update', asyncHandler(async (req, res) => {
    // Capability gate
    if (dns.supportsCapability && !dns.supportsCapability('update-check')) {
      return success(res, { updateAvailable: false, message: 'Update check not supported by current DNS provider' });
    }

    try {
      const { server } = req.query;
      if (!server) {
        throw new ValidationError('Server IP required');
      }

      const serverIp = validateDnsServer(server);
      if (!serverIp) {
        throw new ValidationError('Server must be a configured DNS server');
      }

      // Authenticate with admin credentials for update check
      const tokenResult = await dns.getTokenForServer(serverIp, 'admin');
      if (!tokenResult.success) {
        throw new AuthenticationError('DNS authentication failed. Ensure credentials are configured.');
      }

      const dnsPort = siteConfig.dnsServerPort || '5380';
      const url = `http://${serverIp}:${dnsPort}/api/user/checkForUpdate?token=${encodeURIComponent(tokenResult.token)}`;
      log.info('dns', 'Checking DNS update', { server });

      const response = await fetchT(url, {
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'User-Agent': APP.USER_AGENTS.API
        }
      });

      const text = await response.text();

      if (!text || text.trim() === '') {
        return // Error handled by middleware
      }

      const result = JSON.parse(text);

      if (result.status === 'ok') {
        success(res, {
          updateAvailable: result.response.updateAvailable,
          currentVersion: result.response.currentVersion,
          updateVersion: result.response.updateVersion || null,
          updateTitle: result.response.updateTitle || null,
          updateMessage: result.response.updateMessage || null,
          downloadLink: result.response.downloadLink || null,
          instructionsLink: result.response.instructionsLink || null
        });
      } else {
        // Error handled by middleware
      }
    } catch (error) {
      log.error('dns', 'DNS update check error', { error: error.message });
      // Error handled by middleware
    }
  }, 'dns-check-update'));

  // POST /update — Update DNS server (Technitium only)
  router.post('/update', asyncHandler(async (req, res) => {
    // Capability gate
    if (dns.supportsCapability && !dns.supportsCapability('update-check')) {
      return errorResponse(res, 'Server update not supported by current DNS provider', 501);
    }

    try {
      const { server } = req.query;
      if (!server) {
        throw new ValidationError('Server IP required');
      }

      const serverIp = validateDnsServer(server);
      if (!serverIp) {
        throw new ValidationError('Server must be a configured DNS server');
      }

      // Authenticate with admin credentials for update operations
      const tokenResult = await dns.getTokenForServer(serverIp, 'admin');
      if (!tokenResult.success) {
        throw new AuthenticationError('DNS authentication failed. Ensure credentials are configured.');
      }

      const dnsPort = siteConfig.dnsServerPort || '5380';
      // Check if update is available
      const checkResponse = await fetchT(
        `http://${serverIp}:${dnsPort}/api/user/checkForUpdate?token=${encodeURIComponent(tokenResult.token)}`,
        { method: 'GET', headers: { 'Accept': 'application/json' } }
      );

      const checkText = await checkResponse.text();
      if (!checkText || checkText.trim() === '') {
        return // Error handled by middleware
      }
      const checkResult = JSON.parse(checkText);

      if (checkResult.status !== 'ok') {
        return // Error handled by middleware
      }

      if (!checkResult.response.updateAvailable) {
        return success(res, {
          message: 'Already up to date',
          currentVersion: checkResult.response.currentVersion,
          updated: false
        });
      }

      // Technitium v14+ does not have an installUpdate API endpoint.
      // Return the update info with download link so the frontend can guide the user.
      log.info('dns', 'Update available for DNS server', { server, currentVersion: checkResult.response.currentVersion, updateVersion: checkResult.response.updateVersion });

      success(res, {
        message: `Update available: ${checkResult.response.updateVersion}`,
        previousVersion: checkResult.response.currentVersion,
        newVersion: checkResult.response.updateVersion,
        downloadLink: checkResult.response.downloadLink || null,
        instructionsLink: checkResult.response.instructionsLink || null,
        updated: false,
        manualUpdateRequired: true
      });
    } catch (error) {
      log.error('dns', 'DNS update error', { error: error.message });
      // Error handled by middleware
    }
  }, 'dns-update'));

  // ===== DNS PROPAGATION =====

  // GET /propagation — Get all recent DNS propagation checks
  router.get('/propagation', asyncHandler(async (req, res) => {
    if (!dnsPropagationChecker) {
      return success(res, { verifications: [], message: 'DNS propagation checker not available' });
    }

    // Cleanup old entries
    dnsPropagationChecker.cleanup();

    const verifications = dnsPropagationChecker.getAllVerifications();
    success(res, { verifications });
  }, 'dns-propagation-all'));

  // POST /propagation/verify — Manually trigger DNS propagation verification
  router.post('/propagation/verify', asyncHandler(async (req, res) => {
    if (!dnsPropagationChecker) {
      return errorResponse(res, 'DNS propagation checker not available', 503);
    }

    const { domain, expectedIp } = req.body;

    if (!domain || !expectedIp) {
      throw new ValidationError('domain and expectedIp are required');
    }

    // Validate domain format
    if (!REGEX.DOMAIN.test(domain)) {
      throw new ValidationError('[DC-301] Invalid domain format');
    }

    // Validate IP address
    const validatorLib = require('validator');
    if (!validatorLib.isIP(expectedIp)) {
      throw new ValidationError('[DC-210] Invalid IP address');
    }

    const job = dnsPropagationChecker.startVerification(domain, expectedIp);
    success(res, {
      message: 'DNS propagation verification started',
      domain,
      expectedIp,
      status: job.status
    });
  }, 'dns-propagation-verify'));

  // GET /propagation/:domain — Get propagation status for a specific domain
  router.get('/propagation/:domain', asyncHandler(async (req, res) => {
    if (!dnsPropagationChecker) {
      return success(res, { verification: null, message: 'DNS propagation checker not available' });
    }

    const { domain } = req.params;
    const status = dnsPropagationChecker.getVerificationStatus(domain);

    if (!status) {
      throw new NotFoundError(`No propagation check found for domain: ${domain}`);
    }

    success(res, { verification: status });
  }, 'dns-propagation-domain'));

  return router;
};
