package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

// generateAngieConfigsWithoutReload генерирует конфиги Angie БЕЗ автоматического reload
func generateAngieConfigsWithoutReload(config Config, clientTitle string, resources []ResourceData, resourcePortMap map[int][]PortMapping) (bool, error) {
	log.Printf("Генерация конфигов Angie для %s", clientTitle)

	configDir := "/etc/angie/http.d"
	configFile := filepath.Join(configDir, fmt.Sprintf("angie-ptaf-%s.conf", clientTitle))
	hashFile := configFile + ".hash"

	if err := os.MkdirAll(configDir, 0755); err != nil {
		return false, fmt.Errorf("ошибка создания директории %s: %w", configDir, err)
	}

	tmpFile := configFile + ".tmp"
	f, err := os.Create(tmpFile)
	if err != nil {
		return false, fmt.Errorf("ошибка создания временного файла %s: %w", tmpFile, err)
	}
	defer f.Close()

	// Заголовок
	f.WriteString("# angie configuration file\n")
	f.WriteString("# inserts into http location\n\n")

	// Map для WebSocket
	f.WriteString("# Map для автоматического определения Connection header\n")
	f.WriteString("# Для WebSocket: Connection: upgrade, для HTTP: Connection: '' (пустое для keepalive)\n")
	f.WriteString("map $http_upgrade $proxy_connection {\n")
	f.WriteString("    default upgrade;\n")
	f.WriteString("    ''      '';\n")
	f.WriteString("}\n\n")

	for _, res := range resources {
		containerPorts, ok := resourcePortMap[res.L7ResourceID]
		if !ok {
			os.Remove(tmpFile)
			return false, fmt.Errorf("не найдены порты для l7ResourceID %d", res.L7ResourceID)
		}

		err := writeAngieResourceConfig(f, config, res, containerPorts)
		if err != nil {
			os.Remove(tmpFile)
			return false, fmt.Errorf("ошибка генерации конфига для l7ResourceID %d: %w", res.L7ResourceID, err)
		}
	}

	f.Close()

	newHash, err := getFileHash(tmpFile)
	if err != nil {
		os.Remove(tmpFile)
		return false, fmt.Errorf("ошибка вычисления SHA256: %w", err)
	}

	oldHashBytes, err := os.ReadFile(hashFile)
	oldHash := ""
	if err == nil {
		oldHash = strings.TrimSpace(string(oldHashBytes))
	}

	if oldHash == newHash {
		log.Printf("  ℹ Конфигурация Angie не изменилась (SHA256: %s), reload не требуется", newHash[:12])
		os.Remove(tmpFile)
		return false, nil
	}

	if err := os.Rename(tmpFile, configFile); err != nil {
		os.Remove(tmpFile)
		return false, fmt.Errorf("ошибка замены конфига: %w", err)
	}

	if err := os.WriteFile(hashFile, []byte(newHash), 0644); err != nil {
		log.Printf("  ⚠ Не удалось сохранить хеш: %v", err)
	}

	if oldHash == "" {
		log.Printf("  ✓ Конфиг Angie создан: %s (БЕЗ reload)", configFile)
	} else {
		log.Printf("  ✓ Конфиг Angie обновлён: %s (SHA256: %s → %s) (БЕЗ reload)", configFile, oldHash[:12], newHash[:12])
	}

	return true, nil
}

// generateAngieConfigs генерирует конфигурационные файлы Angie с автоматическим reload
func generateAngieConfigs(config Config, clientTitle string, resources []ResourceData, resourcePortMap map[int][]PortMapping) error {
	changed, err := generateAngieConfigsWithoutReload(config, clientTitle, resources, resourcePortMap)
	if err != nil {
		return err
	}

	if changed {
		reloadAngie() // Существующая функция из containers_part2.go (без возврата error)
	}

	return nil
}

// writeAngieResourceConfig записывает конфигурацию для одного ресурса в Angie
func writeAngieResourceConfig(f *os.File, config Config, res ResourceData, containerPorts []PortMapping) error {
	allDomains := []string{res.ServerName}
	allDomains = append(allDomains, res.Aliases...)
	processedDomains := processDomainsWithWWW(allDomains)

	// Парсим кастомные SERVER_BLOCK
	var customServerBlocks []CustomServerBlock
	var serverDirectivesWithoutBlocks string

	if res.AppsSettings.ServerAngieCustom.Valid && strings.TrimSpace(res.AppsSettings.ServerAngieCustom.String) != "" {
		serverDirectivesRaw := strings.TrimSpace(res.AppsSettings.ServerAngieCustom.String)
		// Для Angie НЕ санитизируем домены в SERVER_BLOCK (принимаем my_site.com как есть)
		customServerBlocks = parseCustomServerBlocks(serverDirectivesRaw, false)

		if len(customServerBlocks) > 0 {
			log.Printf("    → Найдено %d кастомных SERVER_BLOCK директив для Angie l7ResourceID %d",
				len(customServerBlocks), res.L7ResourceID)
			for _, block := range customServerBlocks {
				log.Printf("    → SERVER_BLOCK для Angie порта %d: %s + %d alias(es)",
					block.Port, block.ServerName, len(block.Aliases))
			}
			serverDirectivesWithoutBlocks = stripServerBlocks(serverDirectivesRaw)
		} else {
			serverDirectivesWithoutBlocks = serverDirectivesRaw
		}
	}

	serverBlockDomainsByPort, allServerBlockDomains := collectServerBlockDomains(customServerBlocks)

	if len(allServerBlockDomains) > 0 {
		log.Printf("    → Домены в SERVER_BLOCK для ЛЮБЫХ портов Angie: %v", getMapKeys(allServerBlockDomains))
	}

	hasCustomLocation := res.AppsSettings.LocationAngieCustom.Valid && strings.TrimSpace(res.AppsSettings.LocationAngieCustom.String) != ""
	customLocationStr := ""
	if hasCustomLocation {
		customLocationStr = strings.TrimSpace(res.AppsSettings.LocationAngieCustom.String)
	}

	httpPortsToListen := getInputHTTPPorts(res.AppsSettings)
	httpsPortsToListen := getInputHTTPSPorts(res.AppsSettings)

	// Upstream блоки для HTTPS
	for _, customPort := range httpsPortsToListen {
		upstreamName := fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, customPort, res.ServerName)
		f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
		for _, ports := range containerPorts {
			if dockerPort, ok := ports.HTTPSPorts[customPort]; ok {
				f.WriteString(fmt.Sprintf("    server 127.0.0.1:%d weight=50;\n", dockerPort))
			}
		}
		writeCustomUpstreamOrDefault(f, res.AppsSettings.UpstreamAngieCustom, res.AppsSettings.SNI)
		f.WriteString("}\n\n")
	}

	// Upstream блоки для HTTP
	for _, customPort := range httpPortsToListen {
		upstreamName := fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, customPort, res.ServerName)
		f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
		for _, ports := range containerPorts {
			if dockerPort, ok := ports.HTTPPorts[customPort]; ok {
				f.WriteString(fmt.Sprintf("    server 127.0.0.1:%d weight=50;\n", dockerPort))
			}
		}
		writeCustomUpstreamOrDefault(f, res.AppsSettings.UpstreamAngieCustom, res.AppsSettings.SNI)
		f.WriteString("}\n\n")
	}

	// Upstream блоки для кастомных SERVER_BLOCK в Angie.
	// В nginx upstream для SERVER_BLOCK генерируется отдельным циклом (строки ~577),
	// здесь реализуем аналогичную логику для Angie.
	// Логика выбора docker-порта зеркалит resolveOutputPort из nginx:
	//   1. Если block.Port совпадает с одним из httpsPortsToListen — берём его docker-порт
	//   2. Иначе фоллбэк на httpsPortsToListen[0] — детерминировано, без итерации по map
	if len(customServerBlocks) > 0 {
		log.Printf("    → Генерация upstream'ов для %d кастомных SERVER_BLOCK (Angie) l7ResourceID %d",
			len(customServerBlocks), res.L7ResourceID)
		for _, block := range customServerBlocks {
			var upstreamName string
			if block.IsHTTPS {
				upstreamName = fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, block.Port, block.ServerName)
				f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
				// Определяем input-порт для поиска docker-порта: точное совпадение или фоллбэк на [0]
				resolvedHTTPSPort := block.Port
				foundHTTPS := false
				for _, p := range httpsPortsToListen {
					if p == block.Port {
						foundHTTPS = true
						break
					}
				}
				if !foundHTTPS && len(httpsPortsToListen) > 0 {
					resolvedHTTPSPort = httpsPortsToListen[0]
					log.Printf("      └─ SERVER_BLOCK порт %d не в custom_input_https_ports, фоллбэк на %d",
						block.Port, resolvedHTTPSPort)
				}
				for _, ports := range containerPorts {
					if dockerPort, ok := ports.HTTPSPorts[resolvedHTTPSPort]; ok {
						f.WriteString(fmt.Sprintf("    server 127.0.0.1:%d weight=50;\n", dockerPort))
					}
				}
			} else {
				upstreamName = fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, block.Port, block.ServerName)
				f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
				resolvedHTTPPort := block.Port
				foundHTTP := false
				for _, p := range httpPortsToListen {
					if p == block.Port {
						foundHTTP = true
						break
					}
				}
				if !foundHTTP && len(httpPortsToListen) > 0 {
					resolvedHTTPPort = httpPortsToListen[0]
					log.Printf("      └─ SERVER_BLOCK порт %d не в custom_input_http_ports, фоллбэк на %d",
						block.Port, resolvedHTTPPort)
				}
				for _, ports := range containerPorts {
					if dockerPort, ok := ports.HTTPPorts[resolvedHTTPPort]; ok {
						f.WriteString(fmt.Sprintf("    server 127.0.0.1:%d weight=50;\n", dockerPort))
					}
				}
			}
			writeCustomUpstreamOrDefault(f, res.AppsSettings.UpstreamAngieCustom, res.AppsSettings.SNI)
			f.WriteString("}\n\n")
			log.Printf("      └─ Angie upstream для SERVER_BLOCK: %s", upstreamName)
		}
	}

	// Server блоки для HTTP
	for _, httpPort := range httpPortsToListen {
		defaultDomains := filterDomainsForPort(httpPort, processedDomains, serverBlockDomainsByPort, allServerBlockDomains)
		if len(defaultDomains) > 0 {
			log.Printf("    → Angie HTTP порт %d: default server с %d доменами", httpPort, len(defaultDomains))
			upstreamName := fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, httpPort, res.ServerName)

			f.WriteString("server {\n")
			f.WriteString(fmt.Sprintf("    listen %d;\n", httpPort))
			f.WriteString(fmt.Sprintf("    server_name %s;\n\n", strings.Join(defaultDomains, " ")))
			f.WriteString(fmt.Sprintf("    access_log /var/log/angie/%d_%s_access.log waf;\n", res.L7ResourceID, res.ServerName))
			f.WriteString(fmt.Sprintf("    error_log /var/log/angie/%d_%s_error.log error;\n\n", res.L7ResourceID, res.ServerName))

			writeNetworkSettings(f, config)
			writeStandardServerSettings(f, getCustomServerDirectives(res.AppsSettings.ServerDirectivesAngieCustom))

			if hasCustomLocation {
				log.Printf("    └─ Добавление location_angie_custom (отдельный блок) для l7ResourceID %d (HTTP, порт %d)", res.L7ResourceID, httpPort)
				correctUpstream := fmt.Sprintf("http://%s", upstreamName)
				writeCustomLocationBlock(f, customLocationStr, correctUpstream, res.L7ResourceID, "HTTP", httpPort)
			}

			f.WriteString("    location / {\n")
			if res.AppsSettings.ServerAngieCustom.Valid && serverDirectivesWithoutBlocks != "" {
				log.Printf("    └─ Добавление server_angie_custom для l7ResourceID %d (HTTP location /)", res.L7ResourceID)
				writeIndentedLines(f, serverDirectivesWithoutBlocks)
			}
			f.WriteString(fmt.Sprintf("        proxy_pass http://%s;\n", upstreamName))
			f.WriteString("    }\n")
			f.WriteString("}\n\n")
		} else {
			log.Printf("    → Angie HTTP порт %d: default server НЕ создан (нет доменов после исключения SERVER_BLOCK)", httpPort)
		}
	}

	// Server блоки для HTTPS
	for _, httpsPort := range httpsPortsToListen {
		defaultDomains := filterDomainsForPort(httpsPort, processedDomains, serverBlockDomainsByPort, allServerBlockDomains)
		if len(defaultDomains) > 0 {
			sslEnabled := true
			if res.AppsSettings.SSLEnabled.Valid {
				sslEnabled = res.AppsSettings.SSLEnabled.Bool
			}

			if sslEnabled {
				log.Printf("    → Angie HTTPS порт %d: default server с %d доменами (SSL включен)", httpsPort, len(defaultDomains))
			} else {
				log.Printf("    → Angie HTTPS порт %d: default server с %d доменами (SSL отключен, plain HTTP)", httpsPort, len(defaultDomains))
			}

			upstreamName := fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, httpsPort, res.ServerName)

			f.WriteString("server {\n")
			if sslEnabled {
				f.WriteString(fmt.Sprintf("    listen %d ssl;\n", httpsPort))
			} else {
				f.WriteString(fmt.Sprintf("    listen %d;\n", httpsPort))
			}
			f.WriteString(fmt.Sprintf("    server_name %s;\n\n", strings.Join(defaultDomains, " ")))
			f.WriteString(fmt.Sprintf("    access_log /var/log/angie/%d_%s_access.log waf;\n", res.L7ResourceID, res.ServerName))
			f.WriteString(fmt.Sprintf("    error_log /var/log/angie/%d_%s_error.log error;\n\n", res.L7ResourceID, res.ServerName))

			if sslEnabled {
				writeAngieSSLSettings(f, res.AppsSettings.CustomAngieSSL)
			}

			writeNetworkSettings(f, config)
			writeStandardServerSettings(f, getCustomServerDirectives(res.AppsSettings.ServerDirectivesAngieCustom))

			if hasCustomLocation {
				log.Printf("    └─ Добавление location_angie_custom (отдельный блок) для l7ResourceID %d (HTTPS, порт %d)", res.L7ResourceID, httpsPort)
				correctUpstream := fmt.Sprintf("http://%s", upstreamName)
				writeCustomLocationBlock(f, customLocationStr, correctUpstream, res.L7ResourceID, "HTTPS", httpsPort)
			}

			f.WriteString("    location / {\n")
			if res.AppsSettings.ServerAngieCustom.Valid && serverDirectivesWithoutBlocks != "" {
				log.Printf("    └─ Добавление server_angie_custom для l7ResourceID %d (HTTPS location /)", res.L7ResourceID)
				writeIndentedLines(f, serverDirectivesWithoutBlocks)
			}
			f.WriteString(fmt.Sprintf("        proxy_pass http://%s;\n", upstreamName))
			f.WriteString("    }\n")
			f.WriteString("}\n\n")
		} else {
			log.Printf("    → Angie HTTPS порт %d: default server НЕ создан (нет доменов после исключения SERVER_BLOCK)", httpsPort)
		}
	}

	// Кастомные SERVER_BLOCK
	if len(customServerBlocks) > 0 {
		log.Printf("    → Генерация %d кастомных server блоков для Angie l7ResourceID %d",
			len(customServerBlocks), res.L7ResourceID)
		for _, block := range customServerBlocks {
			aliasInfo := ""
			if len(block.Aliases) > 0 {
				aliasInfo = fmt.Sprintf(" + %d alias(es)", len(block.Aliases))
			}
			log.Printf("      └─ Angie Server блок: порт %d, server_name %s%s",
				block.Port, block.ServerName, aliasInfo)
			generateAngieCustomServerBlock(f, config, res, block, containerPorts, customLocationStr)
		}
	}

	return nil
}

// writeNetworkSettings записывает WAF и Antibot networks для Angie
func writeNetworkSettings(f *os.File, config Config) {
	f.WriteString("    # waf networks\n")
	for _, net := range config.WAFNetworks {
		f.WriteString(fmt.Sprintf("    set_real_ip_from %s;\n", net))
	}
	f.WriteString("    # antibot networks\n")
	for _, net := range config.AntibotNetworks {
		f.WriteString(fmt.Sprintf("    set_real_ip_from %s;\n", net))
	}
	f.WriteString("\n    real_ip_header X-Forwarded-For;\n")
	f.WriteString("    real_ip_recursive on;\n\n")
}

// writeAngieSSLSettings записывает SSL настройки для Angie
func writeAngieSSLSettings(f *os.File, customSSL sql.NullString) {
	if customSSL.Valid && strings.TrimSpace(customSSL.String) != "" {
		log.Printf("    └─ Использование custom_angie_ssl (кастомные SSL настройки)")
		lines := strings.Split(strings.TrimSpace(customSSL.String), "\n")
		for _, line := range lines {
			trimmed := strings.TrimSpace(line)
			if trimmed != "" {
				f.WriteString("    " + trimmed + "\n")
			}
		}
		f.WriteString("\n")
	} else {
		f.WriteString("    ssl_certificate /etc/ssl/certs/sp.crt;\n")
		f.WriteString("    ssl_certificate_key /etc/ssl/private/sp.key;\n")
		f.WriteString("    ssl_protocols TLSv1.3 TLSv1.2;\n")
		f.WriteString("    ssl_prefer_server_ciphers on;\n\n")
	}
}

// writeStandardServerSettings записывает стандартные настройки сервера (Angie)
func writeStandardServerSettings(f *os.File, customDirectives string) {
	settings := `    server_tokens off;
    sendfile on;
    gzip on;
    client_max_body_size 0;

    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    proxy_http_version 1.1;

    js_set $safe_host main.sanitizeHost;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Connection $proxy_connection;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Host $safe_host;
    proxy_set_header X-Original-Host $host;
    proxy_pass_header Date;
    proxy_pass_header Server;
`
	f.WriteString(settings)
	writeCustomOrDefaultDirectives(f, customDirectives)
}

// writeNginxStandardSettings записывает стандартные настройки сервера (Nginx)
func writeNginxStandardSettings(f *os.File, customDirectives string) {
	settings := `    server_tokens off;
    sendfile on;
    gzip on;
    client_max_body_size 0;

    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    proxy_http_version 1.1;

    #proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Connection $proxy_connection;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Host $backend_host;
    proxy_pass_header Date;
    proxy_pass_header Server;
`
	f.WriteString(settings)
	writeCustomOrDefaultDirectives(f, customDirectives)
}

// writeCustomOrDefaultDirectives записывает кастомные или дефолтные директивы таймаутов/буферов
func writeCustomOrDefaultDirectives(f *os.File, customDirectives string) {
	if customDirectives != "" {
		f.WriteString("\n    # Custom server directives (override defaults)\n")
		lines := strings.Split(strings.TrimSpace(customDirectives), "\n")
		for _, line := range lines {
			trimmed := strings.TrimSpace(line)
			if trimmed != "" {
				f.WriteString("    " + trimmed + "\n")
			}
		}
	} else {
		f.WriteString("\n    # Default timeouts and buffers\n")
		f.WriteString("    proxy_connect_timeout 60s;\n")
		f.WriteString("    proxy_read_timeout 120s;\n")
		f.WriteString("    proxy_send_timeout 120s;\n")
		f.WriteString("    send_timeout 60s;\n")
		f.WriteString("    keepalive_timeout 80s;\n")
		f.WriteString("    keepalive_requests 1000;\n")
		f.WriteString("    resolver_timeout 30s;\n")
		f.WriteString("    large_client_header_buffers 4 128k;\n")
		f.WriteString("    proxy_buffers 4 32k;\n")
		f.WriteString("    proxy_buffer_size 16k;\n")
		f.WriteString("    proxy_busy_buffers_size 32k;\n")
	}
}

// generateAngieCustomServerBlock генерирует кастомный server блок для Angie
func generateAngieCustomServerBlock(f *os.File, config Config, res ResourceData, block CustomServerBlock, containerPorts []PortMapping, customLocationStr string) {
	var upstreamName string
	if block.IsHTTPS {
		upstreamName = fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, block.Port, res.ServerName)
	} else {
		upstreamName = fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, block.Port, res.ServerName)
	}

	f.WriteString("server {\n")

	sslEnabled := true
	if res.AppsSettings.SSLEnabled.Valid {
		sslEnabled = res.AppsSettings.SSLEnabled.Bool
	}

	if block.IsHTTPS {
		if sslEnabled {
			f.WriteString(fmt.Sprintf("    listen %d ssl;\n", block.Port))
			writeAngieSSLSettings(f, res.AppsSettings.CustomAngieSSL)
		} else {
			f.WriteString(fmt.Sprintf("    listen %d;\n", block.Port))
		}
	} else {
		f.WriteString(fmt.Sprintf("    listen %d;\n", block.Port))
	}

	if len(block.Aliases) > 0 {
		f.WriteString(fmt.Sprintf("    server_name %s %s;\n\n",
			block.ServerName, strings.Join(block.Aliases, " ")))
	} else {
		f.WriteString(fmt.Sprintf("    server_name %s;\n\n", block.ServerName))
	}

	f.WriteString(fmt.Sprintf("    access_log /var/log/angie/%d_%s_port%d_access.log waf;\n",
		res.L7ResourceID, sanitizeServerName(block.ServerName), block.Port))
	f.WriteString(fmt.Sprintf("    error_log /var/log/angie/%d_%s_port%d_error.log error;\n\n",
		res.L7ResourceID, sanitizeServerName(block.ServerName), block.Port))

	writeNetworkSettings(f, config)
	writeStandardServerSettings(f, getCustomServerDirectives(res.AppsSettings.ServerDirectivesAngieCustom))

	// Кастомные location блоки из location_angie_custom (перед location /)
	if customLocationStr != "" {
		correctUpstream := fmt.Sprintf("http://%s", upstreamName)
		log.Printf("    └─ Добавление location_angie_custom в SERVER_BLOCK порт %d: %s", block.Port, block.ServerName)
		writeCustomLocationBlock(f, customLocationStr, correctUpstream, res.L7ResourceID, "SERVER_BLOCK", block.Port)
	}

	f.WriteString("    location / {\n")
	lines := strings.Split(block.Content, "\n")
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed != "" {
			f.WriteString("        " + trimmed + "\n")
		}
	}
	f.WriteString(fmt.Sprintf("        proxy_pass http://%s;\n", upstreamName))
	f.WriteString("    }\n")
	f.WriteString("}\n\n")
}

// writeUpstreamServers записывает серверы для upstream блока nginx (с origins)
func writeUpstreamServers(f *os.File, origins []OriginItem, outputPort int, settings AppsSettings) {
	allBackup := true
	for _, origin := range origins {
		if strings.ToLower(origin.Mode) != "backup" {
			allBackup = false
			break
		}
	}

	for _, origin := range origins {
		serverLine := fmt.Sprintf("    server %s:%d", origin.IP, outputPort)
		if origin.Weight > 0 {
			serverLine += fmt.Sprintf(" weight=%d", origin.Weight)
		}
		if strings.ToLower(origin.Mode) == "backup" && !allBackup {
			serverLine += " backup"
		}
		maxFails := getMaxFails(settings)
		failTimeout := getFailTimeout(settings)
		serverLine += fmt.Sprintf(" max_fails=%d fail_timeout=%ds", maxFails, failTimeout)
		serverLine += ";\n"
		f.WriteString(serverLine)
	}
}

// generateNginxResourceConfig генерирует конфигурацию nginx для ресурса
func generateNginxResourceConfig(filepath string, config Config, res ResourceData, ports PortMapping) error {
	allDomains := []string{res.ServerName}
	allDomains = append(allDomains, res.Aliases...)
	allDomains = sanitizeDomainsForNginx(allDomains)
	processedDomains := processDomainsWithWWW(allDomains)

	serverNameForProcessing := processedDomains[0]
	aliasesForProcessing := processedDomains[1:]

	f, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer f.Close()

	f.WriteString("# nginx configuration file\n")
	f.WriteString("# inserts into http location\n\n")

	// Парсим SERVER_BLOCK
	var customServerBlocks []CustomServerBlock
	var serverDirectivesWithoutBlocks string

	if res.AppsSettings.ServerNginxCustom.Valid && strings.TrimSpace(res.AppsSettings.ServerNginxCustom.String) != "" {
		serverDirectivesRaw := strings.TrimSpace(res.AppsSettings.ServerNginxCustom.String)
		// Для Nginx санитизируем домены в SERVER_BLOCK (ожидаем my-site.com от Angie)
		customServerBlocks = parseCustomServerBlocks(serverDirectivesRaw, true)

		if len(customServerBlocks) > 0 {
			log.Printf("    → Найдено %d кастомных SERVER_BLOCK директив для l7ResourceID %d",
				len(customServerBlocks), res.L7ResourceID)
			for _, block := range customServerBlocks {
				log.Printf("    → SERVER_BLOCK для порта %d: %s + %d alias(es)",
					block.Port, block.ServerName, len(block.Aliases))
			}
			serverDirectivesWithoutBlocks = stripServerBlocks(serverDirectivesRaw)
		} else {
			serverDirectivesWithoutBlocks = serverDirectivesRaw
		}
		serverDirectivesWithoutBlocks = replaceMTLSPaths(serverDirectivesWithoutBlocks)
	}

	serverBlockDomainsByPort, allServerBlockDomains := collectServerBlockDomains(customServerBlocks)

	if len(allServerBlockDomains) > 0 {
		log.Printf("    → Домены в SERVER_BLOCK для ЛЮБЫХ портов: %v", getMapKeys(allServerBlockDomains))
	}

	// Функция фильтрации для nginx (использует serverName + aliases отдельно)
	filterNginxDomainsForPort := func(port int) []string {
		var defaultDomains []string
		allDoms := append([]string{serverNameForProcessing}, aliasesForProcessing...)
		for _, domain := range allDoms {
			if serverBlockDomainsByPort[port][domain] {
				continue
			}
			if port == 80 || port == 443 {
				defaultDomains = append(defaultDomains, domain)
			} else {
				if !allServerBlockDomains[domain] {
					defaultDomains = append(defaultDomains, domain)
				}
			}
		}
		return defaultDomains
	}

	// Location directives
	var locationDirectives string
	if res.AppsSettings.LocationNginxCustom.Valid && strings.TrimSpace(res.AppsSettings.LocationNginxCustom.String) != "" {
		locationDirectives = replaceMTLSPaths(strings.TrimSpace(res.AppsSettings.LocationNginxCustom.String))
	}

	httpsPortsToListen := getInputHTTPSPorts(res.AppsSettings)
	httpPortsToListen := getInputHTTPPorts(res.AppsSettings)
	httpsOutputPorts := getOutputHTTPSPorts(res.AppsSettings)
	httpOutputPorts := getOutputHTTPPorts(res.AppsSettings)

	hasCustomNginxLocation := locationDirectives != ""
	isFullNginxLocationBlock := hasCustomNginxLocation && strings.Contains(locationDirectives, "location ")

	// Upstream блоки для HTTPS
	for idx, inputPort := range httpsPortsToListen {
		outputPort := httpsOutputPorts[0]
		if idx < len(httpsOutputPorts) {
			outputPort = httpsOutputPorts[idx]
		}
		upstreamName := fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, inputPort, res.ServerName)
		f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
		writeUpstreamServers(f, res.Origins, outputPort, res.AppsSettings)
		writeCustomUpstreamOrDefault(f, res.AppsSettings.UpstreamNginxCustom, res.AppsSettings.SNI)
		f.WriteString("}\n\n")
	}

	// Upstream блоки для HTTP
	for idx, inputPort := range httpPortsToListen {
		outputPort := httpOutputPorts[0]
		if idx < len(httpOutputPorts) {
			outputPort = httpOutputPorts[idx]
		}
		upstreamName := fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, inputPort, res.ServerName)
		f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
		writeUpstreamServers(f, res.Origins, outputPort, res.AppsSettings)
		writeCustomUpstreamOrDefault(f, res.AppsSettings.UpstreamNginxCustom, res.AppsSettings.SNI)
		f.WriteString("}\n\n")
	}

	// Upstream для SERVER_BLOCK
	if len(customServerBlocks) > 0 {
		log.Printf("    → Генерация upstream'ов для %d кастомных server блоков", len(customServerBlocks))
		for _, block := range customServerBlocks {
			outputPort := resolveOutputPort(block, httpsPortsToListen, httpsOutputPorts, httpPortsToListen, httpOutputPorts)
			prefix := map[bool]string{true: "secure", false: "unsecure"}[block.IsHTTPS]
			upstreamName := fmt.Sprintf("%s%d_%d_%s", prefix, res.L7ResourceID, block.Port, block.ServerName)
			f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
			writeUpstreamServers(f, res.Origins, outputPort, res.AppsSettings)
			writeCustomUpstreamOrDefault(f, res.AppsSettings.UpstreamNginxCustom, res.AppsSettings.SNI)
			f.WriteString("}\n\n")
		}
	}

	// Server блоки HTTP
	for _, httpPort := range httpPortsToListen {
		defaultDomains := filterNginxDomainsForPort(httpPort)
		if len(defaultDomains) > 0 {
			log.Printf("    → HTTP порт %d: default server с %d доменами", httpPort, len(defaultDomains))
			upstreamName := fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, httpPort, res.ServerName)

			f.WriteString("server {\n")
			f.WriteString(fmt.Sprintf("    listen %d;\n", httpPort))
			f.WriteString(fmt.Sprintf("    server_name %s;\n\n", strings.Join(defaultDomains, " ")))
			f.WriteString(fmt.Sprintf("    access_log /var/log/nginx/%d_%s_access.log waf;\n", res.L7ResourceID, res.ServerName))
			f.WriteString(fmt.Sprintf("    error_log /var/log/nginx/%d_%s_error.log error;\n\n", res.L7ResourceID, res.ServerName))
			f.WriteString("    set_real_ip_from 172.16.0.0/12;\n")
			f.WriteString("    real_ip_header X-Forwarded-For;\n")
			f.WriteString("    real_ip_recursive on;\n\n")
			writeNginxStandardSettings(f, getCustomServerDirectives(res.AppsSettings.ServerDirectivesNginxCustom))

			if hasCustomNginxLocation && isFullNginxLocationBlock {
				log.Printf("    └─ Добавление location_nginx_custom (отдельный блок) для l7ResourceID %d (HTTP, порт %d)", res.L7ResourceID, httpPort)
				writeCustomLocationBlock(f, locationDirectives, fmt.Sprintf("http://%s", upstreamName), res.L7ResourceID, "HTTP", httpPort)
			}

			f.WriteString("    location / {\n")
			if serverDirectivesWithoutBlocks != "" {
				writeIndentedLines(f, serverDirectivesWithoutBlocks)
			}
			if hasCustomNginxLocation && !isFullNginxLocationBlock {
				writeIndentedLines(f, locationDirectives)
			}
			f.WriteString(fmt.Sprintf("        proxy_pass http://%s;\n", upstreamName))
			f.WriteString("    }\n")
			f.WriteString("}\n\n")
		} else {
			log.Printf("    → HTTP порт %d: default server НЕ создан (нет доменов после исключения SERVER_BLOCK)", httpPort)
		}
	}

	// Server блоки HTTPS
	for _, httpsPort := range httpsPortsToListen {
		defaultDomains := filterNginxDomainsForPort(httpsPort)
		if len(defaultDomains) > 0 {
			log.Printf("    → HTTPS порт %d: default server с %d доменами", httpsPort, len(defaultDomains))
			upstreamName := fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, httpsPort, res.ServerName)

			f.WriteString("server {\n")
			f.WriteString(fmt.Sprintf("    listen %d;\n", httpsPort))
			f.WriteString(fmt.Sprintf("    server_name %s;\n\n", strings.Join(defaultDomains, " ")))
			f.WriteString(fmt.Sprintf("    access_log /var/log/nginx/%d_%s_access.log waf;\n", res.L7ResourceID, res.ServerName))
			f.WriteString(fmt.Sprintf("    error_log /var/log/nginx/%d_%s_error.log error;\n\n", res.L7ResourceID, res.ServerName))
			f.WriteString("    proxy_ssl_server_name on;\n")
			f.WriteString("    proxy_ssl_name $host;\n\n")
			f.WriteString("    set_real_ip_from 172.16.0.0/12;\n")
			f.WriteString("    real_ip_header X-Forwarded-For;\n")
			f.WriteString("    real_ip_recursive on;\n\n")
			writeNginxStandardSettings(f, getCustomServerDirectives(res.AppsSettings.ServerDirectivesNginxCustom))

			if hasCustomNginxLocation && isFullNginxLocationBlock {
				log.Printf("    └─ Добавление location_nginx_custom (отдельный блок) для l7ResourceID %d (HTTPS, порт %d)", res.L7ResourceID, httpsPort)
				writeCustomLocationBlock(f, locationDirectives, fmt.Sprintf("https://%s", upstreamName), res.L7ResourceID, "HTTPS", httpsPort)
			}

			f.WriteString("    location / {\n")
			if serverDirectivesWithoutBlocks != "" {
				writeIndentedLines(f, serverDirectivesWithoutBlocks)
			}
			if hasCustomNginxLocation && !isFullNginxLocationBlock {
				writeIndentedLines(f, locationDirectives)
			}
			f.WriteString(fmt.Sprintf("        proxy_pass https://%s;\n", upstreamName))
			f.WriteString("    }\n")
			f.WriteString("}\n")
		} else {
			log.Printf("    → HTTPS порт %d: default server НЕ создан (нет доменов после исключения SERVER_BLOCK)", httpsPort)
		}
	}

	// Кастомные SERVER_BLOCK
	if len(customServerBlocks) > 0 {
		log.Printf("    → Генерация %d кастомных server блоков для l7ResourceID %d",
			len(customServerBlocks), res.L7ResourceID)
		for _, block := range customServerBlocks {
			aliasInfo := ""
			if len(block.Aliases) > 0 {
				aliasInfo = fmt.Sprintf(" + %d alias(es)", len(block.Aliases))
			}
			log.Printf("      └─ Server блок: порт %d, server_name %s%s", block.Port, block.ServerName, aliasInfo)
			outputPort := resolveOutputPort(block, httpsPortsToListen, httpsOutputPorts, httpPortsToListen, httpOutputPorts)
			generateCustomServerBlock(f, res, block, outputPort, locationDirectives)
		}
	}

	return nil
}

// resolveOutputPort определяет OUTPUT порт для SERVER_BLOCK
func resolveOutputPort(block CustomServerBlock, httpsInput, httpsOutput, httpInput, httpOutput []int) int {
	if block.IsHTTPS {
		for idx, inputPort := range httpsInput {
			if inputPort == block.Port && idx < len(httpsOutput) {
				return httpsOutput[idx]
			}
		}
		if len(httpsOutput) > 0 {
			return httpsOutput[0]
		}
		return 443
	}

	for idx, inputPort := range httpInput {
		if inputPort == block.Port && idx < len(httpOutput) {
			return httpOutput[idx]
		}
	}
	if len(httpOutput) > 0 {
		return httpOutput[0]
	}
	return 80
}

// generateCustomServerBlock генерирует отдельный server блок для nginx из SERVER_BLOCK
func generateCustomServerBlock(f *os.File, res ResourceData, block CustomServerBlock, outputPort int, locationDirectives string) {
	protocol := "http"
	if block.IsHTTPS {
		protocol = "https"
	}

	prefix := map[bool]string{true: "secure", false: "unsecure"}[block.IsHTTPS]
	upstreamName := fmt.Sprintf("%s%d_%d_%s", prefix, res.L7ResourceID, block.Port, block.ServerName)

	f.WriteString("server {\n")
	f.WriteString(fmt.Sprintf("    listen %d;\n", block.Port))

	if len(block.Aliases) > 0 {
		f.WriteString(fmt.Sprintf("    server_name %s %s;\n\n",
			block.ServerName, strings.Join(block.Aliases, " ")))
	} else {
		f.WriteString(fmt.Sprintf("    server_name %s;\n\n", block.ServerName))
	}

	f.WriteString(fmt.Sprintf("    access_log /var/log/nginx/%d_%s_port%d_access.log waf;\n",
		res.L7ResourceID, sanitizeServerName(block.ServerName), block.Port))
	f.WriteString(fmt.Sprintf("    error_log /var/log/nginx/%d_%s_port%d_error.log error;\n\n",
		res.L7ResourceID, sanitizeServerName(block.ServerName), block.Port))

	f.WriteString("    set_real_ip_from 172.16.0.0/12;\n")
	f.WriteString("    real_ip_header X-Forwarded-For;\n")
	f.WriteString("    real_ip_recursive on;\n\n")

	writeNginxStandardSettings(f, getCustomServerDirectives(res.AppsSettings.ServerDirectivesNginxCustom))

	// Кастомные location блоки из location_nginx_custom (перед location /)
	if locationDirectives != "" && strings.Contains(locationDirectives, "location ") {
		correctUpstream := fmt.Sprintf("%s://%s", protocol, upstreamName)
		log.Printf("    └─ Добавление location_nginx_custom в SERVER_BLOCK порт %d: %s", block.Port, block.ServerName)
		writeCustomLocationBlock(f, locationDirectives, correctUpstream, res.L7ResourceID, "SERVER_BLOCK", block.Port)
	}

	f.WriteString("    location / {\n")
	lines := strings.Split(block.Content, "\n")
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed != "" {
			f.WriteString("        " + trimmed + "\n")
		}
	}
	// Если locationDirectives содержит директивы без location-блока — вставляем в location /
	if locationDirectives != "" && !strings.Contains(locationDirectives, "location ") {
		writeIndentedLines(f, locationDirectives)
	}
	f.WriteString(fmt.Sprintf("        proxy_pass %s://%s;\n", protocol, upstreamName))
	f.WriteString("    }\n")
	f.WriteString("}\n\n")
}
