// Package infra gère l'infrastructure (serveurs, containers, nginx).
package infra

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
)

const (
	// NginxSitesAvailable est le répertoire des sites disponibles.
	NginxSitesAvailable = "/etc/nginx/sites-available"
	// NginxSitesEnabled est le répertoire des sites activés.
	NginxSitesEnabled = "/etc/nginx/sites-enabled"
)

// NginxSiteInfo représente les infos d'un site Nginx.
type NginxSiteInfo struct {
	Name     string
	Enabled  bool
	Config   string
	HasSSL   bool
	Domains  []string
	Upstream string
}

// TestNginxConfig teste la configuration Nginx.
func (c *SSHClient) TestNginxConfig(ctx context.Context) error {
	result, err := c.Exec(ctx, "nginx -t")
	if err != nil {
		return fmt.Errorf("nginx test: %w", err)
	}
	// nginx -t écrit sur stderr même en cas de succès
	if result.ExitCode != 0 {
		return fmt.Errorf("nginx config invalid: %s", result.Stderr)
	}
	return nil
}

// ReloadNginx recharge la configuration Nginx.
func (c *SSHClient) ReloadNginx(ctx context.Context) error {
	// D'abord tester la config
	if err := c.TestNginxConfig(ctx); err != nil {
		return err
	}

	result, err := c.Exec(ctx, "systemctl reload nginx")
	if err != nil {
		return fmt.Errorf("nginx reload: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("nginx reload failed: %s", result.Stderr)
	}
	return nil
}

// RestartNginx redémarre Nginx.
func (c *SSHClient) RestartNginx(ctx context.Context) error {
	result, err := c.Exec(ctx, "systemctl restart nginx")
	if err != nil {
		return fmt.Errorf("nginx restart: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("nginx restart failed: %s", result.Stderr)
	}
	return nil
}

// NginxStatus retourne le statut de Nginx.
func (c *SSHClient) NginxStatus(ctx context.Context) (string, error) {
	result, err := c.Exec(ctx, "systemctl is-active nginx")
	if err != nil {
		return "unknown", nil
	}
	return strings.TrimSpace(result.Stdout), nil
}

// ListNginxSites liste les sites Nginx configurés.
func (c *SSHClient) ListNginxSites(ctx context.Context) ([]NginxSiteInfo, error) {
	// Lister sites-available
	result, err := c.Exec(ctx, fmt.Sprintf("ls -1 %s", NginxSitesAvailable))
	if err != nil {
		return nil, fmt.Errorf("list sites-available: %w", err)
	}

	if result.ExitCode != 0 {
		return nil, fmt.Errorf("list sites-available failed: %s", result.Stderr)
	}

	// Lister sites-enabled
	enabledResult, err := c.Exec(ctx, fmt.Sprintf("ls -1 %s", NginxSitesEnabled))
	if err != nil {
		return nil, fmt.Errorf("list sites-enabled: %w", err)
	}

	enabledSet := make(map[string]bool)
	for _, name := range strings.Split(enabledResult.Stdout, "\n") {
		name = strings.TrimSpace(name)
		if name != "" {
			enabledSet[name] = true
		}
	}

	var sites []NginxSiteInfo
	for _, name := range strings.Split(result.Stdout, "\n") {
		name = strings.TrimSpace(name)
		if name == "" || name == "default" {
			continue
		}

		site := NginxSiteInfo{
			Name:    name,
			Enabled: enabledSet[name],
		}
		sites = append(sites, site)
	}

	return sites, nil
}

// GetNginxSiteConfig récupère la config d'un site.
func (c *SSHClient) GetNginxSiteConfig(ctx context.Context, name string) (string, error) {
	path := filepath.Join(NginxSitesAvailable, name)
	content, err := c.ReadFile(ctx, path)
	if err != nil {
		return "", fmt.Errorf("read site config: %w", err)
	}
	return string(content), nil
}

// WriteNginxSiteConfig écrit la config d'un site.
func (c *SSHClient) WriteNginxSiteConfig(ctx context.Context, name string, config string) error {
	path := filepath.Join(NginxSitesAvailable, name)
	return c.WriteFile(ctx, path, []byte(config), "644")
}

// EnableNginxSite active un site (crée le lien symbolique).
func (c *SSHClient) EnableNginxSite(ctx context.Context, name string) error {
	src := filepath.Join(NginxSitesAvailable, name)
	dst := filepath.Join(NginxSitesEnabled, name)

	// Vérifier que le site existe
	exists, err := c.FileExists(ctx, src)
	if err != nil {
		return err
	}
	if !exists {
		return fmt.Errorf("site %s does not exist", name)
	}

	// Créer le lien
	result, err := c.Exec(ctx, fmt.Sprintf("ln -sf %s %s", src, dst))
	if err != nil {
		return fmt.Errorf("enable site: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("enable site failed: %s", result.Stderr)
	}

	return nil
}

// DisableNginxSite désactive un site (supprime le lien symbolique).
func (c *SSHClient) DisableNginxSite(ctx context.Context, name string) error {
	path := filepath.Join(NginxSitesEnabled, name)

	result, err := c.Exec(ctx, fmt.Sprintf("rm -f %s", path))
	if err != nil {
		return fmt.Errorf("disable site: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("disable site failed: %s", result.Stderr)
	}

	return nil
}

// DeleteNginxSite supprime un site (désactive puis supprime).
func (c *SSHClient) DeleteNginxSite(ctx context.Context, name string) error {
	// D'abord désactiver
	if err := c.DisableNginxSite(ctx, name); err != nil {
		return err
	}

	// Puis supprimer
	path := filepath.Join(NginxSitesAvailable, name)
	result, err := c.Exec(ctx, fmt.Sprintf("rm -f %s", path))
	if err != nil {
		return fmt.Errorf("delete site: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("delete site failed: %s", result.Stderr)
	}

	return nil
}

// DeployNginxSite déploie un site complet (écrire, activer, recharger).
func (c *SSHClient) DeployNginxSite(ctx context.Context, name string, config string) error {
	// Écrire la config
	if err := c.WriteNginxSiteConfig(ctx, name, config); err != nil {
		return fmt.Errorf("write config: %w", err)
	}

	// Activer le site
	if err := c.EnableNginxSite(ctx, name); err != nil {
		return fmt.Errorf("enable site: %w", err)
	}

	// Recharger Nginx
	if err := c.ReloadNginx(ctx); err != nil {
		// En cas d'erreur, désactiver le site
		c.DisableNginxSite(ctx, name)
		return fmt.Errorf("reload nginx: %w", err)
	}

	return nil
}

// GenerateNginxProxyConfig génère une config proxy standard.
func GenerateNginxProxyConfig(domain, upstream string, ssl bool) string {
	var config strings.Builder

	if ssl {
		// Redirect HTTP to HTTPS
		config.WriteString(fmt.Sprintf(`server {
    listen 80;
    listen [::]:80;
    server_name %s;
    return 301 https://$server_name$request_uri;
}

`, domain))
	}

	// Main server block
	if ssl {
		config.WriteString(fmt.Sprintf(`server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name %s;

    ssl_certificate /etc/letsencrypt/live/%s/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/%s/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    location / {
        proxy_pass %s;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}
`, domain, domain, domain, upstream))
	} else {
		config.WriteString(fmt.Sprintf(`server {
    listen 80;
    listen [::]:80;
    server_name %s;

    location / {
        proxy_pass %s;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}
`, domain, upstream))
	}

	return config.String()
}

// RequestSSLCertificate demande un certificat Let's Encrypt.
func (c *SSHClient) RequestSSLCertificate(ctx context.Context, domain, email string) error {
	cmd := fmt.Sprintf("certbot certonly --nginx -d %s --non-interactive --agree-tos -m %s", domain, email)
	result, err := c.Exec(ctx, cmd)
	if err != nil {
		return fmt.Errorf("certbot: %w", err)
	}
	if result.ExitCode != 0 {
		return fmt.Errorf("certbot failed: %s", result.Stderr)
	}
	return nil
}

// CheckSSLCertificate vérifie si un certificat SSL existe.
func (c *SSHClient) CheckSSLCertificate(ctx context.Context, domain string) (bool, error) {
	path := fmt.Sprintf("/etc/letsencrypt/live/%s/fullchain.pem", domain)
	return c.FileExists(ctx, path)
}
