package main

import (
	"database/sql"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"

	"github.com/joho/godotenv"
	"github.com/lib/pq"
)

// Константы для graceful shutdown
const (
	// Время ожидания перед удалением контейнера при масштабировании вниз (в секундах)
	// Должно быть больше чем keepalive_timeout в nginx (обычно 60-75 секунд)
	ContainerDrainTimeout = 90

	// Время ожидания перед удалением контейнера при миграции между хостами (в секундах)
	// Увеличенное время для безопасного переключения трафика на новый хост
	MigrationDrainTimeout = 3600 // 1 час

	// Максимальное количество контейнеров на одного клиента
	MaxContainersPerClient = 20
)

// Константы диапазонов портов для Docker
const (
	HTTPPortStart  = 18000 // Начало диапазона HTTP портов
	HTTPPortEnd    = 20999 // Конец диапазона HTTP портов (3000 портов)
	HTTPSPortStart = 21000 // Начало диапазона HTTPS портов
	HTTPSPortEnd   = 23999 // Конец диапазона HTTPS портов (3000 портов)
)

// Пути к файлам конфигурации
const (
	EnvFilePath = "/etc/magos/magos.env"
)

// Константы конфигурации по умолчанию (используются как fallback если env не загружен)
const (
	DefaultDBName            = "waf_info"
	DefaultDBPort            = 5432
	DefaultPrimaryDBHost     = "10.100.10.8"
	DefaultSecondaryDBHost   = "10.100.13.5"
	DefaultDockerImage       = "ptaf-core-nginx-agent:release-4.1.6.409465"
	DefaultWorkerProcesses   = 4
	DefaultWorkerConnections = 64000
)

// Конфигурация приложения
type Config struct {
	DBUser            string
	DBPassword        string
	DBName            string
	DBPort            int
	PrimaryDBHost     string
	SecondaryDBHost   string
	APIToken          string
	AntibotNetworks   []string
	WAFNetworks       []string
	WorkerProcesses   int
	WorkerConnections int
	DockerImage       string
}

// loadEnvFile загружает переменные окружения из файла /etc/magos/magos.env
func loadEnvFile() error {
	if err := godotenv.Load(EnvFilePath); err != nil {
		return fmt.Errorf("ошибка загрузки %s: %w", EnvFilePath, err)
	}
	log.Printf("✓ Загружен файл конфигурации: %s", EnvFilePath)
	return nil
}

// getEnv возвращает значение переменной окружения или дефолтное значение
func getEnv(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return defaultValue
}

// getEnvInt возвращает int значение переменной окружения или дефолтное значение
func getEnvInt(key string, defaultValue int) int {
	if value := os.Getenv(key); value != "" {
		if intVal, err := strconv.Atoi(value); err == nil {
			return intVal
		}
		log.Printf("⚠ Некорректное значение %s=%s, используется дефолт %d", key, value, defaultValue)
	}
	return defaultValue
}

// getDefaultConfig возвращает конфигурацию из env-файла или дефолтные значения
func getDefaultConfig() Config {
	// Пытаемся загрузить env файл (не критично если не получится)
	if err := loadEnvFile(); err != nil {
		log.Printf("⚠ %v", err)
		log.Printf("  Используются дефолтные значения для DB_USER, DB_PASSWORD, API_TOKEN")
	}

	return Config{
		DBUser:          getEnv("DB_USER", ""),
		DBPassword:      getEnv("DB_PASSWORD", ""),
		DBName:          getEnv("DB_NAME", DefaultDBName),
		DBPort:          getEnvInt("DB_PORT", DefaultDBPort),
		PrimaryDBHost:   getEnv("PRIMARY_DB_HOST", DefaultPrimaryDBHost),
		SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
		APIToken:        getEnv("API_TOKEN", ""),
		AntibotNetworks: []string{
			"185.66.85.0/24",
			"185.66.86.0/24",
			"185.35.5.0/24",
			"185.35.6.0/24",
			"212.67.26.0/24",
		},
		WAFNetworks: []string{
			"109.238.89.0/24",
			"89.20.63.0/24",
		},
		WorkerProcesses:   getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
		WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
		DockerImage:       getEnv("DOCKER_IMAGE", DefaultDockerImage),
	}
}

// initConfig инициализирует конфигурацию, загружая IP списки из БД
func initConfig(db *sql.DB) (Config, error) {
	// Читаем IP адреса и сети из таблицы ips
	antibotNetworks, wafNetworks, err := loadIPsFromDB(db)
	if err != nil {
		log.Printf("⚠ Не удалось загрузить IP настройки из БД: %v", err)
		log.Printf("  Используются значения по умолчанию")
		return getDefaultConfig(), nil
	}

	// Загружаем env файл для получения credentials
	if err := loadEnvFile(); err != nil {
		log.Printf("⚠ %v", err)
		log.Printf("  Используются дефолтные значения для DB_USER, DB_PASSWORD, API_TOKEN")
	}

	// Формируем Config с настройками из env + IP из БД
	config := Config{
		DBUser:            getEnv("DB_USER", ""),
		DBPassword:        getEnv("DB_PASSWORD", ""),
		DBName:            getEnv("DB_NAME", DefaultDBName),
		DBPort:            getEnvInt("DB_PORT", DefaultDBPort),
		PrimaryDBHost:     getEnv("PRIMARY_DB_HOST", DefaultPrimaryDBHost),
		SecondaryDBHost:   getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
		APIToken:          getEnv("API_TOKEN", ""),
		DockerImage:       getEnv("DOCKER_IMAGE", DefaultDockerImage),
		WorkerProcesses:   getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
		WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
		AntibotNetworks:   antibotNetworks,
		WAFNetworks:       wafNetworks,
	}

	// Валидация критичных параметров
	if config.DBUser == "" {
		log.Printf("⚠ DB_USER не установлен в %s", EnvFilePath)
	}
	if config.DBPassword == "" {
		log.Printf("⚠ DB_PASSWORD не установлен в %s", EnvFilePath)
	}
	if config.APIToken == "" {
		log.Printf("⚠ API_TOKEN не установлен в %s", EnvFilePath)
	}

	log.Printf("✓ IP конфигурация загружена из таблицы ips")
	log.Printf("  - Antibot Networks: %d сетей", len(config.AntibotNetworks))
	log.Printf("  - WAF Networks: %d сетей", len(config.WAFNetworks))

	return config, nil
}

// loadIPsFromDB загружает списки IP адресов и сетей из таблицы ips
func loadIPsFromDB(db *sql.DB) (antibotNetworks []string, wafNetworks []string, err error) {
	query := `
		SELECT antibot_networks, waf_networks
		FROM ips
		WHERE id = 1
	`

	// PostgreSQL массивы читаем через pq.Array
	var antibotNetworksArray, wafNetworksArray []string

	err = db.QueryRow(query).Scan(
		pq.Array(&antibotNetworksArray),
		pq.Array(&wafNetworksArray),
	)

	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil, fmt.Errorf("записи с id=1 не найдено в таблице ips")
		}
		return nil, nil, fmt.Errorf("ошибка чтения из таблицы ips: %w", err)
	}

	return antibotNetworksArray, wafNetworksArray, nil
}

// isIPInWAFNetworks проверяет, входит ли IP адрес в одну из WAF подсетей
func isIPInWAFNetworks(ipStr string, wafNetworks []string) bool {
	ip := net.ParseIP(ipStr)
	if ip == nil {
		return false
	}

	for _, cidr := range wafNetworks {
		_, ipNet, err := net.ParseCIDR(cidr)
		if err != nil {
			continue
		}

		if ipNet.Contains(ip) {
			return true
		}
	}

	return false
}
