const fs = require('fs');
const path = require('path');
const { DEFAULT_PORTS } = require('../shared/constants');

/**
 * CaddyfileGenerator - Creates Caddyfile and docker-compose.yml for DashCaddy
 *
 * Generates production-grade configs that match the patterns used by the
 * running DashCaddy deployment (CORS snippets, admin origins, PKI, etc.)
 */
class CaddyfileGenerator {
  /**
   * Normalize a Windows path to forward slashes for Caddyfile/Docker Compose
   */
  _p(s) {
    return s.replace(/\\/g, '/');
  }

  /**
   * Generate the CORS snippet block used by the API
   */
  _corsSnippets() {
    return `(cors-preflight) {
  @preflight method OPTIONS
  header @preflight {
    Access-Control-Allow-Origin "*"
    Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
    Access-Control-Allow-Headers "*"
    Access-Control-Max-Age "600"
  }
  respond @preflight 204
}
(cors-allow) {
  header {
    Access-Control-Allow-Origin "*"
    Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
    Access-Control-Allow-Headers "*"
    Access-Control-Max-Age "600"
  }
}
`;
  }

  /**
   * Generate the dashcaddy_auth snippet for SSO
   */
  _authSnippet(apiPort) {
    return `# DashCaddy SSO auth snippet
(dashcaddy_auth) {
  forward_auth localhost:${apiPort} {
    uri /api/v1/auth/gate/{args[0]}
    copy_headers Authorization X-Api-Key X-App-Cookie X-Emby-Token X-Plex-Token
  }
}
`;
  }

  /**
   * Generate the dashboard site block content (shared across all modes)
   */
  _dashboardBlock(dashboardPath, tier, apiPort) {
    let block = '';

    block += `  root * ${this._p(dashboardPath)}\n`;
    block += `  encode gzip\n\n`;

    // API proxy for intermediate/advanced tiers
    if (tier !== 'basic') {
      block += `  # API proxy to Docker container\n`;
      block += `  handle /api/* {\n`;
      block += `    reverse_proxy localhost:${apiPort}\n`;
      block += `  }\n\n`;
    }

    // SPA fallback with file_server
    block += `  # Static site + SPA fallback\n`;
    block += `  handle {\n`;
    block += `    @notFile not file {path}\n`;
    block += `    rewrite @notFile /index.html\n`;
    block += `    file_server\n`;
    block += `  }\n`;

    return block;
  }

  /**
   * Generate Caddyfile for local mode (IP:Port, no TLS)
   */
  generateCaddyfile(options) {
    const {
      dashboardPath,
      port = 8080,
      tier = 'basic',
      apiPort = DEFAULT_PORTS.API
    } = options;

    const adminPort = DEFAULT_PORTS.CADDY_ADMIN;

    let caddyfile = `# DashCaddy Caddyfile - Local Mode
# Generated by DashCaddy Installer

{
  admin localhost:${adminPort} {
    origins localhost localhost:${adminPort} host.docker.internal host.docker.internal:${adminPort}
  }
  auto_https off
}

`;

    if (tier !== 'basic') {
      caddyfile += this._corsSnippets() + '\n';
    }

    caddyfile += `:${port} {\n`;
    caddyfile += this._dashboardBlock(dashboardPath, tier, apiPort);
    caddyfile += `}\n`;

    return caddyfile;
  }

  /**
   * Generate Caddyfile for public domain mode (Let's Encrypt TLS)
   */
  generateCaddyfileWithDomain(options) {
    const {
      dashboardPath,
      domain,
      email = '',
      tier = 'basic',
      apiPort = DEFAULT_PORTS.API
    } = options;

    const adminPort = DEFAULT_PORTS.CADDY_ADMIN;

    let caddyfile = `# DashCaddy Caddyfile - Public Domain Mode
# Generated by DashCaddy Installer

{
  admin localhost:${adminPort} {
    origins localhost localhost:${adminPort} host.docker.internal host.docker.internal:${adminPort}
  }
`;
    if (email) {
      caddyfile += `  email ${email}\n`;
    }
    caddyfile += `}\n\n`;

    if (tier !== 'basic') {
      caddyfile += this._corsSnippets() + '\n';
      caddyfile += this._authSnippet(apiPort) + '\n';
    }

    caddyfile += `${domain} {\n`;
    caddyfile += this._dashboardBlock(dashboardPath, tier, apiPort);
    caddyfile += `}\n`;

    return caddyfile;
  }

  /**
   * Generate Caddyfile for custom TLD mode (internal CA)
   */
  generateCaddyfileCustomTLD(options) {
    const {
      dashboardPath,
      installPath,
      tld = '.home',
      caName = 'DashCaddy Local CA',
      tier = 'basic',
      apiPort = DEFAULT_PORTS.API
    } = options;

    const adminPort = DEFAULT_PORTS.CADDY_ADMIN;
    const dashboardDomain = `dashcaddy${tld}`;
    const certsPath = installPath ? this._p(path.join(installPath, 'certs')) : './certs';

    let caddyfile = `# DashCaddy Caddyfile - Custom TLD Mode (${tld})
# Generated by DashCaddy Installer

{
  storage file_system {
    root ${certsPath}
  }

  admin localhost:${adminPort} {
    origins localhost localhost:${adminPort} host.docker.internal host.docker.internal:${adminPort}
  }

  pki {
    ca local {
      name "${caName}"
      root_cn "${caName} Root CA"
      intermediate_cn "${caName} Intermediate CA"
    }
  }
}

`;

    if (tier !== 'basic') {
      caddyfile += this._corsSnippets() + '\n';
      caddyfile += this._authSnippet(apiPort) + '\n';
    }

    caddyfile += `# Dashboard - ${dashboardDomain}\n`;
    caddyfile += `${dashboardDomain} {\n`;
    caddyfile += `  tls internal\n\n`;
    caddyfile += this._dashboardBlock(dashboardPath, tier, apiPort);
    caddyfile += `}\n`;

    return caddyfile;
  }

  /**
   * Write Caddyfile to disk
   */
  async writeCaddyfile(content, outputPath) {
    try {
      await fs.promises.writeFile(outputPath, content, 'utf8');
      return { success: true, path: outputPath };
    } catch (error) {
      return { success: false, error: error.message };
    }
  }

  /**
   * Create complete Caddyfile setup
   * @param {string} installPath - Installation directory
   * @param {Object} options - Configuration options
   */
  async createCaddyfileSetup(installPath, options = {}) {
    try {
      const dashboardPath = path.join(installPath, 'sites', 'status');
      const caddyfilePath = path.join(installPath, 'Caddyfile');

      let content;
      if (options.domainMode === 'public') {
        content = this.generateCaddyfileWithDomain({
          dashboardPath,
          domain: options.publicDomain,
          email: options.email,
          tier: options.tier,
          apiPort: options.apiPort
        });
      } else if (options.domainMode === 'custom-tld') {
        content = this.generateCaddyfileCustomTLD({
          dashboardPath,
          installPath,
          tld: options.tld,
          caName: options.caName || 'DashCaddy Local CA',
          tier: options.tier,
          apiPort: options.apiPort
        });
      } else {
        content = this.generateCaddyfile({
          dashboardPath,
          ...options
        });
      }

      const result = await this.writeCaddyfile(content, caddyfilePath);

      if (!result.success) {
        throw new Error(result.error);
      }

      return {
        success: true,
        path: caddyfilePath,
        content
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  /**
   * Generate docker-compose.yml for running the API server
   * @param {string} installPath - Installation directory
   * @param {Object} options - Configuration options
   * @param {number} options.apiPort - API server port
   * @param {string} options.lanIP - Host LAN IP address
   * @param {string} options.tailscaleIP - Host Tailscale IP address
   * @param {string} options.domainMode - Domain mode (local, public, custom-tld)
   */
  generateDockerCompose(installPath, options = {}) {
    const apiPort = options.apiPort || DEFAULT_PORTS.API;
    const adminPort = DEFAULT_PORTS.CADDY_ADMIN;
    const p = this._p.bind(this);

    // Core volume mounts
    let volumes = `      - ${p(installPath)}/Caddyfile:/caddyfile:rw
      - ${p(installPath)}/services.json:/app/services.json:rw
      - ${p(installPath)}/dns-credentials.json:/app/dns-credentials.json:rw
      - ${p(installPath)}/config.json:/app/config.json:rw
      - ${p(installPath)}/credentials.json:/app/credentials.json:rw
      - ${p(installPath)}/.encryption-key:/app/.encryption-key:rw
      - ${p(installPath)}/notifications.json:/app/notifications.json:rw
      - ${p(installPath)}/sites/status/assets:/app/assets:rw
      - /var/run/docker.sock:/var/run/docker.sock`;

    // Add CA cert mount for custom-tld mode
    if (options.domainMode === 'custom-tld') {
      volumes += `\n      - ${p(installPath)}/certs/pki/authorities/local:/app/pki:ro`;
    }

    // Environment variables
    let envVars = `      - CADDYFILE_PATH=/caddyfile
      - CADDY_ADMIN_URL=http://host.docker.internal:${adminPort}
      - ASSETS_PATH=/app/assets
      - CREDENTIALS_FILE=/app/credentials.json
      - NODE_ENV=production`;

    if (options.domainMode === 'custom-tld') {
      envVars += `\n      - CA_CERT_PATH=/app/pki/root.crt`;
    }

    if (options.lanIP) {
      envVars += `\n      - HOST_LAN_IP=${options.lanIP}`;
    }
    if (options.tailscaleIP) {
      envVars += `\n      - HOST_TAILSCALE_IP=${options.tailscaleIP}`;
    }

    const dockerCompose = `services:
  dashcaddy-api:
    build: .
    container_name: dashcaddy-api
    ports:
      - "${apiPort}:${apiPort}"
    volumes:
${volumes}
    environment:
${envVars}
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped
`;

    return dockerCompose;
  }

  /**
   * Write docker-compose.yml to disk
   * @param {string} installPath - Installation directory
   * @param {Object} options - Configuration options
   */
  async createDockerCompose(installPath, options = {}) {
    try {
      const content = this.generateDockerCompose(installPath, options);
      const apiDir = path.join(installPath, 'sites', 'dashcaddy-api');
      await fs.promises.mkdir(apiDir, { recursive: true });
      const outputPath = path.join(apiDir, 'docker-compose.yml');

      await fs.promises.writeFile(outputPath, content, 'utf8');

      return {
        success: true,
        path: outputPath,
        content
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
}

module.exports = CaddyfileGenerator;
