package main

import (
	"database/sql"
	"fmt"
	"log"
	"net"
	"strings"
	"time"
)

// processPTAFClient обрабатывает клиента для PTAF vendor
// Вся логика из processClient() перенесена сюда без изменений
func processPTAFClient(
	db *sql.DB,
	config Config,
	hostname, instanceName string,
	clientInfo ClientInfo,
	appsSettingsList []AppsSettings,
	portAllocator *PortAllocator,
) bool {
	log.Printf("Количество контейнеров: %d", clientInfo.ContainersCount)
	if clientInfo.PTAFConfig.Valid {
		log.Printf("PTAF Config: установлен (скрыт для безопасности)")
	} else {
		log.Printf("PTAF Config: не установлен")
	}
	if clientInfo.FluentBitPort.Valid {
		log.Printf("Fluent Bit Port: %d", clientInfo.FluentBitPort.Int64)
	}

	// Проверяем принадлежность хоста к instance клиента
	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 — инициируем миграцию
	if clientInfo.WAFInstance != "" && !belongsToInstance {
		handleClientMigration(clientInfo, hostname)
		return false
	}

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

	// Автоочистка устаревших маркеров drain
	cleanStaleDrainMarkers(clientInfo)

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

	// Выделение портов
	resourcePortMap, err := resolvePortsForClient(clientInfo, resourcesData, portAllocator)
	if err != nil {
		log.Printf("Ошибка выделения портов для клиента %s: %v", clientInfo.ClientTitle, err)
		return false
	}

	// ════════════════════════════════════════════════════════════
	// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Изменен порядок операций
	// БЫЛО: generateAngieConfigs (с reload) → setupContainers
	// СТАЛО: generateAngieConfigs (БЕЗ reload) → setupContainers → проверка портов → reload Angie
	// ════════════════════════════════════════════════════════════

	// 1. Генерация конфигурации Angie БЕЗ автоматического reload
	angieChanged, err := generateAngieConfigsWithoutReload(config, clientInfo.ClientTitle, resourcesData, resourcePortMap)
	if err != nil {
		log.Printf("Ошибка генерации конфигов Angie для %s: %v", clientInfo.ClientTitle, err)
		return false
	}

	// 2. Создание директорий и конфигов для контейнеров (пересоздание если нужно)
	err = setupContainers(config, clientInfo.ClientTitle, &clientInfo, resourcesData, resourcePortMap)
	if err != nil {
		log.Printf("Ошибка настройки контейнеров для %s: %v", clientInfo.ClientTitle, err)
		return false
	}

	// 3. Reload Angie только ПОСЛЕ того как все контейнеры готовы
	if angieChanged {
		log.Printf("  → Конфиг Angie изменился, проверяем готовность контейнеров перед reload")

		// Проверяем что все порты контейнеров слушают
		allPortsReady := true
		totalPorts := 0
		readyPorts := 0

		for _, res := range resourcesData {
			containerPorts, ok := resourcePortMap[res.L7ResourceID]
			if !ok {
				continue
			}

			for _, ports := range containerPorts {
				// Проверяем HTTPS порты
				for customPort, dockerPort := range ports.HTTPSPorts {
					totalPorts++
					if checkPortListening("127.0.0.1", dockerPort) {
						readyPorts++
					} else {
						log.Printf("  ⚠ Порт %d (custom %d) для l7ResourceID %d не слушает!",
							dockerPort, customPort, res.L7ResourceID)
						allPortsReady = false
					}
				}

				// Проверяем HTTP порты
				for customPort, dockerPort := range ports.HTTPPorts {
					totalPorts++
					if checkPortListening("127.0.0.1", dockerPort) {
						readyPorts++
					} else {
						log.Printf("  ⚠ Порт %d (custom %d) для l7ResourceID %d не слушает!",
							dockerPort, customPort, res.L7ResourceID)
						allPortsReady = false
					}
				}
			}
		}

		log.Printf("  → Портов готово: %d/%d", readyPorts, totalPorts)

		if !allPortsReady {
			log.Printf("⚠ Не все порты готовы, пропускаем reload Angie для безопасности")
			log.Printf("  → Angie продолжит работать со старой конфигурацией")
			log.Printf("  → При следующем запуске программы будет повторена попытка")
		} else {
			log.Printf("  ✓ Все порты слушают, безопасно перезагрузить Angie")
			reloadAngie() // Функция из containers_part2.go (не возвращает error)
		}
	} else {
		log.Printf("  ℹ Конфиг Angie не изменился, reload не требуется")
	}
	// ════════════════════════════════════════════════════════════

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

// fetchPTAFResourcesData получает данные для каждого ресурса из кеша или API
// Проверяет что waf_vendor = 'ptaf'
func fetchPTAFResourcesData(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 - обрабатываем только ресурсы PTAF
		if appsSettings.WAFVendor.Valid && strings.ToLower(strings.TrimSpace(appsSettings.WAFVendor.String)) != "ptaf" {
			log.Printf("Пропускаем l7ResourceID %d: waf_vendor = '%s' (требуется 'ptaf')", appsSettings.L7ResourceID, appsSettings.WAFVendor.String)
			continue
		}

		if !appsSettings.WAFVendor.Valid || appsSettings.WAFVendor.String == "" {
			log.Printf("Пропускаем l7ResourceID %d: waf_vendor не указан (требуется 'ptaf')", 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
		}

		if mode == "manual" {
			log.Printf("l7ResourceID %d: mode = 'manual', waf_vendor = 'ptaf' ✓", appsSettings.L7ResourceID)
		} else if appsSettings.Mode.Valid {
			log.Printf("l7ResourceID %d: mode = 'auto', waf_vendor = 'ptaf' ✓", appsSettings.L7ResourceID)
		} else {
			log.Printf("l7ResourceID %d: mode = NULL (обрабатываем как 'auto'), waf_vendor = 'ptaf' ✓", appsSettings.L7ResourceID)
		}

		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: ptaf)")
		} 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))

				// Сохранение в кеш (опционально, можно закомментировать если функция отсутствует)
				// if err := saveResourceDataToCache(db, appsSettings.L7ResourceID, serverName, aliases, origins); err != nil {
				// 	log.Printf("  ⚠ Ошибка сохранения в кеш: %v", err)
				// }
			}
		}

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

	return resourcesData
}

// checkPortListening проверяет что порт слушает на указанном адресе
// Функция уже есть в main.go, но для совместимости оставим здесь тоже
// (Go позволяет дублирование функций в одном package если они приватные)
func checkPortListeningPTAF(host string, port int) bool {
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), 2*time.Second)
	if err != nil {
		return false
	}
	conn.Close()
	return true
}
