package main

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

// SW константы
const (
	SWNginxBinary       = "solidwall-nginx"
	SWConfigDir         = "/etc/solidwall-nginx"
	SWSiteAvailableDir  = "/etc/solidwall-nginx/site-available"
	SWSiteEnabledDir    = "/etc/solidwall-nginx/site-enabled"
	SWStaticDir         = "/etc/solidwall-nginx/static"
	SWLogDir            = "/var/log/solidwall"
	SWInternalHTTPPort  = 8000
	SWInternalHTTPSPort = 9000
	SWSSLCertPath       = "ssl/cert.pem"
	SWSSLKeyPath        = "ssl/key.pem"
)

// processSWClient обрабатывает клиента для SW vendor
func processSWClient(
	db *sql.DB,
	config Config,
	hostname, instanceName string,
	clientInfo ClientInfo,
	appsSettingsList []AppsSettings,
	portAllocator *PortAllocator,
) bool {
	log.Printf("═══════════════════════════════════════════════════════════")
	log.Printf("SW VENDOR: обработка клиента %s", clientInfo.ClientTitle)
	log.Printf("═══════════════════════════════════════════════════════════")

	// Проверяем принадлежность хоста к instance клиента (аналогично PTAF)
	belongsToInstance := false
	if clientInfo.WAFInstance != "" {
		var err error
		belongsToInstance, err = checkHostBelongsToInstance(db, hostname, clientInfo.WAFInstance)
		if err != nil {
			log.Printf("⚠ Ошибка проверки принадлежности хоста для %s: %v", clientInfo.ClientTitle, err)
			return false
		}
	}

	// Если хост НЕ принадлежит instance — пропускаем (миграция для SW не реализована)
	if clientInfo.WAFInstance != "" && !belongsToInstance {
		log.Printf("⚠ Клиент %s должен быть на instance '%s', текущий хост '%s' не принадлежит этому instance",
			clientInfo.ClientTitle, clientInfo.WAFInstance, hostname)
		log.Printf("  SW не поддерживает автоматическую миграцию, пропускаем")
		return false
	}

	// Хост принадлежит instance — обрабатываем
	if clientInfo.WAFInstance == "" {
		log.Printf("ℹ Клиент %s не привязан к конкретному instance, обрабатываю", clientInfo.ClientTitle)
	} else {
		log.Printf("✓ Клиент %s принадлежит instance '%s', обрабатываю", clientInfo.ClientTitle, clientInfo.WAFInstance)
	}

	// Получение данных ресурсов (фильтр по waf_vendor = 'sw')
	resourcesData := fetchSWResourcesData(db, config, appsSettingsList)
	if len(resourcesData) == 0 {
		log.Printf("⚠ Нет ресурсов с данными для клиента %s, пропускаем", clientInfo.ClientTitle)
		return false
	}

	log.Printf("✓ Получено ресурсов для SW: %d", len(resourcesData))

	// Создание static файлов если отсутствуют
	if err := ensureSWStaticFiles(); err != nil {
		log.Printf("⚠ Ошибка создания static файлов: %v", err)
		// Не критично, продолжаем
	}

	// Генерация конфигов для каждого ресурса
	configsChanged := false
	for _, res := range resourcesData {
		changed, err := generateSWConfig(config, clientInfo.ClientTitle, res)
		if err != nil {
			log.Printf("⚠ Ошибка генерации конфига для l7ResourceID %d: %v", res.L7ResourceID, err)
			continue
		}
		if changed {
			configsChanged = true
		}
	}

	// Reload nginx если были изменения
	if configsChanged {
		log.Printf("→ Конфиги SW изменились, выполняю проверку и reload")
		if err := reloadSWNginx(); err != nil {
			log.Printf("⚠ Ошибка reload SW nginx: %v", err)
			return false
		}
		log.Printf("✓ SW nginx успешно перезагружен")
	} else {
		log.Printf("ℹ Конфиги SW не изменились, reload не требуется")
	}

	log.Printf("✓ Клиент %s (SW) успешно обработан", clientInfo.ClientTitle)
	return true
}

// fetchSWResourcesData получает данные для каждого ресурса из кеша или API
// Проверяет что waf_vendor = 'sw'
func fetchSWResourcesData(db *sql.DB, config Config, appsSettingsList []AppsSettings) []ResourceData {
	var resourcesData []ResourceData

	for _, appsSettings := range appsSettingsList {
		if !appsSettings.WAFEnabled.Valid || !appsSettings.WAFEnabled.Bool {
			log.Printf("Пропускаем l7ResourceID %d: waf_enabled = false", appsSettings.L7ResourceID)
			continue
		}

		// Проверяем waf_vendor - обрабатываем только ресурсы SW
		if appsSettings.WAFVendor.Valid && strings.ToLower(strings.TrimSpace(appsSettings.WAFVendor.String)) != "sw" {
			log.Printf("Пропускаем l7ResourceID %d: waf_vendor = '%s' (требуется 'sw')", appsSettings.L7ResourceID, appsSettings.WAFVendor.String)
			continue
		}

		if !appsSettings.WAFVendor.Valid || appsSettings.WAFVendor.String == "" {
			log.Printf("Пропускаем l7ResourceID %d: waf_vendor не указан (требуется 'sw')", appsSettings.L7ResourceID)
			continue
		}

		mode := "auto"
		if appsSettings.Mode.Valid {
			mode = strings.ToLower(strings.TrimSpace(appsSettings.Mode.String))
		}

		if mode != "auto" && mode != "manual" {
			log.Printf("Пропускаем l7ResourceID %d: mode = '%s' (требуется 'auto' или 'manual')", appsSettings.L7ResourceID, mode)
			continue
		}

		log.Printf("l7ResourceID %d: mode = '%s', waf_vendor = 'sw' ✓", appsSettings.L7ResourceID, mode)
		log.Printf("Получение данных для l7ResourceID: %d", appsSettings.L7ResourceID)

		var serverName string
		var aliases []string
		var origins []OriginItem
		var err error

		if mode == "manual" {
			serverName, aliases, origins, err = getResourceDataFromManualInfo(db, config, appsSettings.L7ResourceID)
			if err != nil {
				log.Printf("Ошибка получения данных из manual_info для l7ResourceID %d: %v", appsSettings.L7ResourceID, err)
				continue
			}
			log.Printf("  ✓ Данные получены из manual_info (waf_vendor: sw)")
		} else {
			var found bool
			serverName, aliases, origins, found, err = getResourceDataFromCache(db, config, appsSettings.L7ResourceID)

			if found && err == nil {
				log.Printf("  Server Name: %s", serverName)
				log.Printf("  Aliases: %v", aliases)
				log.Printf("  Origins: %d шт.", len(origins))
			} else {
				if err != nil {
					log.Printf("  ⚠ Ошибка чтения кеша: %v, запрашиваю API", err)
				} else {
					log.Printf("  ℹ Данных нет в кеше, запрашиваю API")
				}

				serverName, err = getServerName(config, appsSettings.L7ResourceID)
				if err != nil {
					log.Printf("Ошибка получения server_name для l7ResourceID %d: %v", appsSettings.L7ResourceID, err)
					continue
				}
				log.Printf("  Server Name: %s", serverName)

				aliases, err = getAliases(config, appsSettings.L7ResourceID)
				if err != nil {
					log.Printf("Ошибка получения aliases для l7ResourceID %d: %v", appsSettings.L7ResourceID, err)
					continue
				}
				log.Printf("  Aliases: %v", aliases)

				origins, err = getOrigins(config, appsSettings.L7ResourceID)
				if err != nil {
					log.Printf("Ошибка получения origins для l7ResourceID %d: %v", appsSettings.L7ResourceID, err)
					continue
				}
				log.Printf("  Origins: %d шт.", len(origins))
			}
		}

		resourcesData = append(resourcesData, ResourceData{
			L7ResourceID: appsSettings.L7ResourceID,
			ServerName:   serverName,
			Aliases:      aliases,
			Origins:      origins,
			AppsSettings: appsSettings,
		})
	}

	return resourcesData
}

// generateSWConfig генерирует nginx конфиг для одного ресурса SW
func generateSWConfig(config Config, clientTitle string, res ResourceData) (bool, error) {
	configFilename := fmt.Sprintf("solidwall-%s-%d.conf", sanitizeFilename(clientTitle), res.L7ResourceID)
	configPath := filepath.Join(SWSiteAvailableDir, configFilename)
	enabledPath := filepath.Join(SWSiteEnabledDir, configFilename)

	log.Printf("  → Генерация конфига SW: %s", configFilename)

	// Создаём временный файл
	tmpPath := configPath + ".tmp"
	f, err := os.Create(tmpPath)
	if err != nil {
		return false, fmt.Errorf("создание временного файла: %w", err)
	}
	defer f.Close()

	// Генерация содержимого конфига
	if err := writeSWConfigContent(f, config, res); err != nil {
		os.Remove(tmpPath)
		return false, fmt.Errorf("запись конфига: %w", err)
	}

	f.Close()

	// Проверяем изменился ли конфиг
	changed := true
	if oldContent, err := os.ReadFile(configPath); err == nil {
		if newContent, err := os.ReadFile(tmpPath); err == nil {
			changed = string(oldContent) != string(newContent)
		}
	}

	// Заменяем старый конфиг новым
	if err := os.Rename(tmpPath, configPath); err != nil {
		os.Remove(tmpPath)
		return false, fmt.Errorf("замена конфига: %w", err)
	}

	// Создаём симлинк в site-enabled если не существует
	if _, err := os.Lstat(enabledPath); os.IsNotExist(err) {
		relPath := fmt.Sprintf("../site-available/%s", configFilename)
		if err := os.Symlink(relPath, enabledPath); err != nil {
			log.Printf("  ⚠ Ошибка создания симлинка: %v", err)
		} else {
			log.Printf("  ✓ Создан симлинк в site-enabled")
		}
	}

	if changed {
		log.Printf("  ✓ Конфиг %s изменён", configFilename)
	} else {
		log.Printf("  ℹ Конфиг %s не изменился", configFilename)
	}

	return changed, nil
}

// writeSWConfigContent записывает содержимое SW nginx конфига
func writeSWConfigContent(f *os.File, config Config, res ResourceData) error {
	// Собираем все домены (server_name + aliases с www)
	allDomains := []string{res.ServerName}
	allDomains = append(allDomains, res.Aliases...)
	processedDomains := processDomainsWithWWW(allDomains)
	domainsStr := strings.Join(processedDomains, " ")

	// Получаем input порты (что слушаем извне)
	httpInputPorts := getInputHTTPPorts(res.AppsSettings)
	httpsInputPorts := getInputHTTPSPorts(res.AppsSettings)

	// Получаем output порты (куда проксируем к origin)
	httpOutputPorts := getOutputHTTPPorts(res.AppsSettings)
	httpsOutputPorts := getOutputHTTPSPorts(res.AppsSettings)

	// Проверяем наличие SERVER_BLOCK в server_nginx_custom
	var customServerBlocks []CustomServerBlock
	var serverDirectivesWithoutBlocks string

	if res.AppsSettings.ServerNginxCustom.Valid && strings.TrimSpace(res.AppsSettings.ServerNginxCustom.String) != "" {
		serverDirectivesRaw := strings.TrimSpace(res.AppsSettings.ServerNginxCustom.String)
		// Для SW не санитизируем домены (работаем как есть)
		customServerBlocks = parseCustomServerBlocks(serverDirectivesRaw, false)

		if len(customServerBlocks) > 0 {
			log.Printf("  → Найдено %d кастомных SERVER_BLOCK директив для SW", len(customServerBlocks))
			serverDirectivesWithoutBlocks = stripServerBlocks(serverDirectivesRaw)
		} else {
			serverDirectivesWithoutBlocks = serverDirectivesRaw
		}
	}

	// location_nginx_custom
	var locationDirectives string
	var isFullLocationBlock bool
	if res.AppsSettings.LocationNginxCustom.Valid && strings.TrimSpace(res.AppsSettings.LocationNginxCustom.String) != "" {
		locationDirectives = strings.TrimSpace(res.AppsSettings.LocationNginxCustom.String)
		isFullLocationBlock = strings.Contains(locationDirectives, "location ")
		log.Printf("  → Обнаружен location_nginx_custom (полный блок: %v)", isFullLocationBlock)
	}

	f.WriteString("# SolidWall WAF configuration\n")
	f.WriteString(fmt.Sprintf("# Client: %s, Resource ID: %d\n", res.ServerName, res.L7ResourceID))
	f.WriteString(fmt.Sprintf("# Generated: %s\n\n", time.Now().Format("2006-01-02 15:04:05")))

	// ════════════════════════════════════════════════════════════
	// UPSTREAM БЛОКИ
	// ════════════════════════════════════════════════════════════

	// HTTPS upstreams (может быть несколько если custom_output_https_ports)
	for idx, outputPort := range httpsOutputPorts {
		upstreamName := fmt.Sprintf("secure%d", res.L7ResourceID)
		if len(httpsOutputPorts) > 1 {
			upstreamName = fmt.Sprintf("secure%d_%d", res.L7ResourceID, outputPort)
		}

		f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
		writeSWUpstreamServers(f, res.Origins, outputPort, res.AppsSettings)

		// Кастомные upstream директивы или дефолтные keepalive
		if idx == 0 { // Применяем кастом только к первому upstream
			writeSWCustomUpstreamOrDefault(f, res.AppsSettings)
		} else {
			writeSWDefaultUpstreamSettings(f, res.AppsSettings)
		}

		f.WriteString("}\n\n")
	}

	// HTTP upstreams
	for idx, outputPort := range httpOutputPorts {
		upstreamName := fmt.Sprintf("unsecure%d", res.L7ResourceID)
		if len(httpOutputPorts) > 1 {
			upstreamName = fmt.Sprintf("unsecure%d_%d", res.L7ResourceID, outputPort)
		}

		f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
		writeSWUpstreamServers(f, res.Origins, outputPort, res.AppsSettings)

		// Кастомные upstream директивы или дефолтные keepalive
		if idx == 0 {
			writeSWCustomUpstreamOrDefault(f, res.AppsSettings)
		} else {
			writeSWDefaultUpstreamSettings(f, res.AppsSettings)
		}

		f.WriteString("}\n\n")
	}

	// ════════════════════════════════════════════════════════════
	// ПУБЛИЧНЫЕ SERVER БЛОКИ (принимают трафик от клиентов)
	// ════════════════════════════════════════════════════════════

	// HTTP servers (порты из custom_input_http_ports или дефолт 80)
	for _, inputPort := range httpInputPorts {
		writeSWPublicHTTPServer(f, config, res, inputPort, domainsStr, serverDirectivesWithoutBlocks, locationDirectives, isFullLocationBlock)
	}

	// HTTPS servers (порты из custom_input_https_ports или дефолт 443)
	for _, inputPort := range httpsInputPorts {
		writeSWPublicHTTPSServer(f, config, res, inputPort, domainsStr, serverDirectivesWithoutBlocks, locationDirectives, isFullLocationBlock)
	}

	// ════════════════════════════════════════════════════════════
	// ВНУТРЕННИЕ SERVER БЛОКИ (проксируют на origins)
	// ════════════════════════════════════════════════════════════

	// Определяем какой upstream использовать (если несколько output портов)
	httpUpstreamName := fmt.Sprintf("unsecure%d", res.L7ResourceID)
	if len(httpOutputPorts) > 1 {
		httpUpstreamName = fmt.Sprintf("unsecure%d_%d", res.L7ResourceID, httpOutputPorts[0])
	}

	httpsUpstreamName := fmt.Sprintf("secure%d", res.L7ResourceID)
	if len(httpsOutputPorts) > 1 {
		httpsUpstreamName = fmt.Sprintf("secure%d_%d", res.L7ResourceID, httpsOutputPorts[0])
	}

	writeSWInternalHTTPServer(f, res, domainsStr, httpUpstreamName)
	writeSWInternalHTTPSServer(f, res, domainsStr, httpsUpstreamName)

	// ════════════════════════════════════════════════════════════
	// КАСТОМНЫЕ SERVER_BLOCK (если есть)
	// ════════════════════════════════════════════════════════════

	if len(customServerBlocks) > 0 {
		log.Printf("  → Генерация %d кастомных SERVER_BLOCK для SW", len(customServerBlocks))
		for _, block := range customServerBlocks {
			generateSWCustomServerBlock(f, config, res, block, locationDirectives)
		}
	}

	return nil
}

// writeSWUpstreamServers записывает серверы в upstream блок
func writeSWUpstreamServers(f *os.File, origins []OriginItem, port 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, port)
		if origin.Weight > 0 {
			serverLine += fmt.Sprintf(" weight=%d", origin.Weight)
		}
		maxFails := getMaxFails(settings)
		failTimeout := getFailTimeout(settings)
		serverLine += fmt.Sprintf("  max_fails=%d fail_timeout=%ds", maxFails, failTimeout)
		if strings.ToLower(origin.Mode) == "backup" && !allBackup {
			serverLine += " backup"
		}
		serverLine += ";\n"
		f.WriteString(serverLine)
	}
}

// writeSWCustomUpstreamOrDefault записывает кастомные или дефолтные upstream директивы
func writeSWCustomUpstreamOrDefault(f *os.File, settings AppsSettings) {
	if settings.UpstreamNginxCustom.Valid && strings.TrimSpace(settings.UpstreamNginxCustom.String) != "" {
		log.Printf("    └─ Использование upstream_nginx_custom")
		lines := strings.Split(strings.TrimSpace(settings.UpstreamNginxCustom.String), "\n")
		for _, line := range lines {
			trimmed := strings.TrimSpace(line)
			if trimmed != "" {
				f.WriteString("    " + trimmed + "\n")
			}
		}
	} else {
		writeSWDefaultUpstreamSettings(f, settings)
	}
}

// writeSWDefaultUpstreamSettings записывает дефолтные upstream настройки
func writeSWDefaultUpstreamSettings(f *os.File, settings AppsSettings) {
	// Проверяем SNI (0 = keepalive включен, 1 = отключен)
	keepaliveEnabled := true
	if settings.SNI.Valid && settings.SNI.Int64 == 1 {
		keepaliveEnabled = false
		log.Printf("    └─ Keepalive отключен (sni = 1)")
	}

	if keepaliveEnabled {
		f.WriteString("    keepalive 60;\n")
		f.WriteString("    keepalive_timeout 70s;\n")
	}
}

// writeSWPublicHTTPServer пишет публичный HTTP server блок
func writeSWPublicHTTPServer(f *os.File, config Config, res ResourceData, port int, domainsStr string, serverDirectives string, locationDirectives string, isFullLocationBlock bool) {
	f.WriteString("# Proxying HTTP requests to SolidWall WAF\n")
	f.WriteString("server {\n")
	f.WriteString("    include static/server.conf;\n")
	f.WriteString(fmt.Sprintf("    listen %d;\n", port))
	f.WriteString(fmt.Sprintf("    server_name %s;\n\n", domainsStr))

	writeSWRealIPConfig(f, config)
	writeSWStandardSettings(f, res.AppsSettings)

	f.WriteString(fmt.Sprintf("    set $BACKEND http://127.0.0.1:%d;\n", SWInternalHTTPPort))

	// Кастомные location блоки (полные) перед location /
	if isFullLocationBlock {
		log.Printf("    └─ Добавление location_nginx_custom (отдельный блок) для HTTP порт %d", port)
		writeIndentedLines(f, locationDirectives)
		f.WriteString("\n")
	}

	f.WriteString("    location / {\n")

	// server_nginx_custom директивы внутри location / (если есть и не SERVER_BLOCK)
	if serverDirectives != "" {
		log.Printf("    └─ Добавление server_nginx_custom в location / (HTTP порт %d)", port)
		writeIndentedLines(f, serverDirectives)
	}

	// location_nginx_custom директивы внутри location / (если это не полный блок)
	if !isFullLocationBlock && locationDirectives != "" {
		log.Printf("    └─ Добавление location_nginx_custom (директивы) в location / (HTTP порт %d)", port)
		writeIndentedLines(f, locationDirectives)
	}

	f.WriteString("        include static/waf.conf;\n")
	f.WriteString("    }\n")
	f.WriteString("}\n\n")
}

// writeSWPublicHTTPSServer пишет публичный HTTPS server блок
func writeSWPublicHTTPSServer(f *os.File, config Config, res ResourceData, port int, domainsStr string, serverDirectives string, locationDirectives string, isFullLocationBlock bool) {
	f.WriteString("# Proxying HTTPS requests to SolidWall WAF\n")
	f.WriteString("server {\n")
	f.WriteString("    include static/server.conf;\n")
	f.WriteString(fmt.Sprintf("    listen %d ssl;\n", port))
	f.WriteString(fmt.Sprintf("    server_name %s;\n\n", domainsStr))

	f.WriteString(fmt.Sprintf("    ssl_certificate %s;\n", SWSSLCertPath))
	f.WriteString(fmt.Sprintf("    ssl_certificate_key %s;\n", SWSSLKeyPath))
	f.WriteString("    ssl_protocols TLSv1.3 TLSv1.2;\n")
	f.WriteString("    ssl_prefer_server_ciphers on;\n\n")

	writeSWRealIPConfig(f, config)
	writeSWStandardSettings(f, res.AppsSettings)

	f.WriteString(fmt.Sprintf("    set $BACKEND http://127.0.0.1:%d;\n", SWInternalHTTPSPort))

	// Кастомные location блоки (полные) перед location /
	if isFullLocationBlock {
		log.Printf("    └─ Добавление location_nginx_custom (отдельный блок) для HTTPS порт %d", port)
		writeIndentedLines(f, locationDirectives)
		f.WriteString("\n")
	}

	f.WriteString("    location / {\n")

	// server_nginx_custom директивы внутри location /
	if serverDirectives != "" {
		log.Printf("    └─ Добавление server_nginx_custom в location / (HTTPS порт %d)", port)
		writeIndentedLines(f, serverDirectives)
	}

	// location_nginx_custom директивы внутри location / (если это не полный блок)
	if !isFullLocationBlock && locationDirectives != "" {
		log.Printf("    └─ Добавление location_nginx_custom (директивы) в location / (HTTPS порт %d)", port)
		writeIndentedLines(f, locationDirectives)
	}

	f.WriteString("        include static/waf.conf;\n")
	f.WriteString("    }\n")
	f.WriteString("}\n\n")
}

// writeSWInternalHTTPServer пишет внутренний HTTP → Origin server блок
func writeSWInternalHTTPServer(f *os.File, res ResourceData, domainsStr string, upstreamName string) {
	f.WriteString("# Proxying HTTP requests to origin\n")
	f.WriteString("server {\n")
	f.WriteString(fmt.Sprintf("    listen 127.0.0.1:%d;\n", SWInternalHTTPPort))
	f.WriteString(fmt.Sprintf("    server_name %s;\n\n", domainsStr))

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

	writeSWStandardSettings(f, res.AppsSettings)

	f.WriteString("    proxy_set_header CDN-Loop \"WAF\";\n\n")
	f.WriteString(fmt.Sprintf("    error_log %s/%d_error.log error;\n\n", SWLogDir, res.L7ResourceID))

	f.WriteString("    location / {\n")
	f.WriteString(fmt.Sprintf("        proxy_pass http://%s;\n", upstreamName))
	f.WriteString("    }\n")
	f.WriteString("}\n\n")
}

// writeSWInternalHTTPSServer пишет внутренний HTTPS → Origin server блок
func writeSWInternalHTTPSServer(f *os.File, res ResourceData, domainsStr string, upstreamName string) {
	f.WriteString("# Proxying HTTPS requests to origin\n")
	f.WriteString("server {\n")
	f.WriteString(fmt.Sprintf("    listen 127.0.0.1:%d;\n", SWInternalHTTPSPort))
	f.WriteString(fmt.Sprintf("    server_name %s;\n\n", domainsStr))

	f.WriteString("    proxy_ssl_server_name on;\n")
	f.WriteString("    proxy_ssl_name $host;\n\n")

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

	writeSWStandardSettings(f, res.AppsSettings)

	f.WriteString("    proxy_set_header CDN-Loop \"WAF\";\n\n")
	f.WriteString(fmt.Sprintf("    error_log %s/%d_error.log error;\n\n", SWLogDir, res.L7ResourceID))

	f.WriteString("    location / {\n")
	f.WriteString(fmt.Sprintf("        proxy_pass https://%s;\n", upstreamName))
	f.WriteString("    }\n")
	f.WriteString("}\n\n")
}

// generateSWCustomServerBlock генерирует кастомный SERVER_BLOCK для SW
func generateSWCustomServerBlock(f *os.File, config Config, res ResourceData, block CustomServerBlock, locationDirectives string) {

	aliasInfo := ""
	if len(block.Aliases) > 0 {
		aliasInfo = fmt.Sprintf(" + %d alias(es)", len(block.Aliases))
	}
	log.Printf("    └─ SW SERVER_BLOCK: порт %d, server_name %s%s", block.Port, block.ServerName, aliasInfo)

	f.WriteString("# Custom SERVER_BLOCK for SW\n")
	f.WriteString("server {\n")
	f.WriteString("    include static/server.conf;\n")

	if block.IsHTTPS {
		f.WriteString(fmt.Sprintf("    listen %d ssl;\n", block.Port))
		f.WriteString(fmt.Sprintf("    ssl_certificate %s;\n", SWSSLCertPath))
		f.WriteString(fmt.Sprintf("    ssl_certificate_key %s;\n", SWSSLKeyPath))
		f.WriteString("    ssl_protocols TLSv1.3 TLSv1.2;\n")
		f.WriteString("    ssl_prefer_server_ciphers on;\n\n")
	} 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))
	}

	writeSWRealIPConfig(f, config)
	writeSWStandardSettings(f, res.AppsSettings)

	internalPort := SWInternalHTTPPort
	if block.IsHTTPS {
		internalPort = SWInternalHTTPSPort
	}
	f.WriteString(fmt.Sprintf("    set $BACKEND http://127.0.0.1:%d;\n", internalPort))

	// Кастомные location блоки (если есть и это полный блок)
	isFullLocationBlock := strings.Contains(locationDirectives, "location ")
	if isFullLocationBlock && locationDirectives != "" {
		log.Printf("      └─ Добавление location_nginx_custom в SERVER_BLOCK")
		writeIndentedLines(f, locationDirectives)
		f.WriteString("\n")
	}

	f.WriteString("    location / {\n")

	// Контент из SERVER_BLOCK тега
	lines := strings.Split(block.Content, "\n")
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed != "" {
			f.WriteString("        " + trimmed + "\n")
		}
	}

	// location_nginx_custom директивы (если это не полный блок)
	if !isFullLocationBlock && locationDirectives != "" {
		writeIndentedLines(f, locationDirectives)
	}

	f.WriteString("        include static/waf.conf;\n")
	f.WriteString("    }\n")
	f.WriteString("}\n\n")
}

// writeSWRealIPConfig пишет real_ip конфигурацию (antibot networks)
func writeSWRealIPConfig(f *os.File, config Config) {
	f.WriteString("    # antibot networks\n")
	for _, net := range config.AntibotNetworks {
		f.WriteString(fmt.Sprintf("    set_real_ip_from %s;\n", net))
	}
	f.WriteString("    real_ip_header X-Forwarded-For;\n")
	f.WriteString("    real_ip_recursive on;\n\n")
}

// writeSWStandardSettings пишет стандартные настройки nginx
func writeSWStandardSettings(f *os.File, settings AppsSettings) {
	// Проверяем есть ли server_directives_nginx_custom
	if settings.ServerDirectivesNginxCustom.Valid && strings.TrimSpace(settings.ServerDirectivesNginxCustom.String) != "" {
		log.Printf("    └─ Использование server_directives_nginx_custom (замена дефолтных настроек)")

		// Пишем только основные директивы (без таймаутов/буферов)
		f.WriteString("    server_tokens off;\n")
		f.WriteString("    sendfile on;\n")
		f.WriteString("    gzip on;\n\n")

		f.WriteString("    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;\n")
		f.WriteString("    proxy_http_version 1.1;\n\n")

		f.WriteString("    #proxy_set_header X-Real-IP $remote_addr;\n")
		f.WriteString("    #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n")
		f.WriteString("    proxy_set_header Connection \"upgrade\";\n")
		f.WriteString("    proxy_set_header Upgrade $http_upgrade;\n")
		f.WriteString("    proxy_set_header Host $host;\n")
		f.WriteString("    proxy_pass_header Date;\n")
		f.WriteString("    proxy_pass_header Server;\n\n")

		// Кастомные директивы (заменяют таймауты/буферы)
		f.WriteString("    # Custom server directives (override defaults)\n")
		lines := strings.Split(strings.TrimSpace(settings.ServerDirectivesNginxCustom.String), "\n")
		for _, line := range lines {
			trimmed := strings.TrimSpace(line)
			if trimmed != "" {
				f.WriteString("    " + trimmed + "\n")
			}
		}
		f.WriteString("\n")
	} else {
		// Дефолтные настройки
		f.WriteString("    server_tokens off;\n")
		f.WriteString("    sendfile on;\n")
		f.WriteString("    gzip on;\n")
		f.WriteString("    client_max_body_size 1g;\n")
		f.WriteString("    large_client_header_buffers 4 128k;\n\n")

		f.WriteString("    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;\n")
		f.WriteString("    proxy_http_version 1.1;\n")
		f.WriteString("    proxy_connect_timeout 3s;\n")
		f.WriteString("    proxy_busy_buffers_size 32k;\n")
		f.WriteString("    proxy_buffers 4 32k;\n")
		f.WriteString("    proxy_buffer_size 16k;\n")
		f.WriteString("    proxy_read_timeout 60s;\n")
		f.WriteString("    proxy_send_timeout 60s;\n")
		f.WriteString("    send_timeout 60s;\n")
		f.WriteString("    keepalive_timeout 75s;\n")
		f.WriteString("    keepalive_requests 1000;\n")
		f.WriteString("    resolver_timeout 30s;\n\n")

		f.WriteString("    #proxy_set_header X-Real-IP $remote_addr;\n")
		f.WriteString("    #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n")
		f.WriteString("    proxy_set_header Connection \"upgrade\";\n")
		f.WriteString("    proxy_set_header Upgrade $http_upgrade;\n")
		f.WriteString("    proxy_set_header Host $host;\n")
		f.WriteString("    proxy_pass_header Date;\n")
		f.WriteString("    proxy_pass_header Server;\n\n")
	}
}

// ensureSWStaticFiles создаёт static файлы если отсутствуют
func ensureSWStaticFiles() error {
	if err := os.MkdirAll(SWStaticDir, 0755); err != nil {
		return fmt.Errorf("создание директории %s: %w", SWStaticDir, err)
	}

	// static/waf.conf
	wafConfPath := filepath.Join(SWStaticDir, "waf.conf")
	if _, err := os.Stat(wafConfPath); os.IsNotExist(err) {
		wafContent := `uninitialized_variable_warn off;
rewrite_by_lua_block { require "solidwall.waf.nginx"() }
content_by_lua_block { require "solidwall.waf.nginx"() }
log_by_lua_block { require "solidwall.waf.nginx"() }
`
		if err := os.WriteFile(wafConfPath, []byte(wafContent), 0644); err != nil {
			return fmt.Errorf("создание waf.conf: %w", err)
		}
		log.Printf("✓ Создан файл: %s", wafConfPath)
	}

	// static/server.conf
	serverConfPath := filepath.Join(SWStaticDir, "server.conf")
	if _, err := os.Stat(serverConfPath); os.IsNotExist(err) {
		serverContent := `root conf/templates;
set $solidwall_request_id "";
set $solidwall_request_decision "";
set $solidwall_request_analysis_time "";
set $solidwall_response_decision "";
set $solidwall_response_analysis_time "";
set $solidwall_backend_response_status_code "";
set $solidwall_backend_header_time "";
set $reason "";
location = /__sw {
    client_max_body_size 8k;
    client_body_buffer_size 8k;
    content_by_lua_block { require "solidwall.waf.nginx.external_api"() }
}
include user/server.conf;
`
		if err := os.WriteFile(serverConfPath, []byte(serverContent), 0644); err != nil {
			return fmt.Errorf("создание server.conf: %w", err)
		}
		log.Printf("✓ Создан файл: %s", serverConfPath)
	}

	return nil
}

// reloadSWNginx выполняет проверку и reload nginx
func reloadSWNginx() error {
	// Проверка конфигурации
	log.Printf("  → Проверка конфигурации SW nginx...")
	testCmd := exec.Command(SWNginxBinary, "-t")
	if output, err := testCmd.CombinedOutput(); err != nil {
		log.Printf("  ✗ Ошибка проверки конфигурации:\n%s", string(output))
		return fmt.Errorf("nginx -t failed: %w", err)
	}
	log.Printf("  ✓ Конфигурация валидна")

	// Reload
	log.Printf("  → Перезагрузка SW nginx...")
	reloadCmd := exec.Command(SWNginxBinary, "-s", "reload")
	if output, err := reloadCmd.CombinedOutput(); err != nil {
		log.Printf("  ✗ Ошибка reload:\n%s", string(output))
		return fmt.Errorf("nginx -s reload failed: %w", err)
	}

	return nil
}

// sanitizeFilename очищает строку для использования в имени файла
func sanitizeFilename(s string) string {
	s = strings.ReplaceAll(s, "/", "_")
	s = strings.ReplaceAll(s, "\\", "_")
	s = strings.ReplaceAll(s, ":", "_")
	s = strings.ReplaceAll(s, "*", "_")
	s = strings.ReplaceAll(s, "?", "_")
	s = strings.ReplaceAll(s, "\"", "_")
	s = strings.ReplaceAll(s, "<", "_")
	s = strings.ReplaceAll(s, ">", "_")
	s = strings.ReplaceAll(s, "|", "_")
	return s
}
