package main

import (
	"database/sql"
	"fmt"
	"log"
	"strings"

	_ "github.com/lib/pq"
)

// connectToDB подключается к базе данных PostgreSQL
// Сначала пытается подключиться к primary, если не удалось - к secondary
func connectToDB(config Config) *sql.DB {
	log.Println("Попытка подключения к первичной БД...")

	connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
		config.PrimaryDBHost, config.DBPort, config.DBUser, config.DBPassword, config.DBName)

	db, err := sql.Open("postgres", connStr)
	if err == nil {
		err = db.Ping()
		if err == nil {
			log.Printf("Успешное подключение к первичной БД: %s", config.PrimaryDBHost)
			return db
		}
	}

	log.Printf("Не удалось подключиться к первичной БД: %v", err)
	log.Println("Попытка подключения к вторичной БД...")

	connStr = fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
		config.SecondaryDBHost, config.DBPort, config.DBUser, config.DBPassword, config.DBName)

	db, err = sql.Open("postgres", connStr)
	if err != nil {
		log.Fatalf("Ошибка открытия соединения с вторичной БД: %v", err)
	}

	err = db.Ping()
	if err != nil {
		log.Fatalf("Ошибка подключения к вторичной БД: %v", err)
	}

	log.Printf("Успешное подключение к вторичной БД: %s", config.SecondaryDBHost)
	return db
}

// getInstancesByHostname получает все instances для hostname из таблицы instances
// Возвращает массив instances, т.к. один hostname может быть связан с несколькими instance
func getInstancesByHostname(db *sql.DB, hostname string) ([]string, error) {
	query := "SELECT instance FROM instances_new WHERE hostname = $1 ORDER BY instance"

	rows, err := db.Query(query, hostname)
	if err != nil {
		return nil, fmt.Errorf("ошибка выполнения запроса для hostname %s: %w", hostname, err)
	}
	defer rows.Close()

	var instances []string
	for rows.Next() {
		var instance string
		if err := rows.Scan(&instance); err != nil {
			return nil, fmt.Errorf("ошибка чтения instance: %w", err)
		}
		instances = append(instances, instance)
	}

	if err = rows.Err(); err != nil {
		return nil, fmt.Errorf("ошибка обработки результатов: %w", err)
	}

	if len(instances) == 0 {
		return nil, fmt.Errorf("instances не найдены для hostname %s", hostname)
	}

	return instances, nil
}

// checkHostBelongsToInstance проверяет принадлежит ли хост к указанному instance
func checkHostBelongsToInstance(db *sql.DB, hostname, instance string) (bool, error) {
	query := `
		SELECT COUNT(*) 
		FROM instances_new 
		WHERE hostname = $1 AND instance = $2
	`

	var count int
	err := db.QueryRow(query, hostname, instance).Scan(&count)
	if err != nil {
		return false, fmt.Errorf("ошибка проверки принадлежности хоста: %w", err)
	}

	return count > 0, nil
}

// getClientInfoByInstance получает всех клиентов для заданного waf_instance
func getClientInfoByInstance(db *sql.DB, instance string) ([]ClientInfo, error) {
	query := `
		SELECT containers_count, ptaf_config, client_title, fluent_bit_port, waf_instance
		FROM client_info
		WHERE waf_instance = $1
	`

	rows, err := db.Query(query, instance)
	if err != nil {
		if strings.Contains(err.Error(), "does not exist") {
			return nil, fmt.Errorf("ошибка структуры таблицы client_info: %w (возможно изменилась схема БД)", err)
		}
		return nil, fmt.Errorf("ошибка выполнения запроса: %w", err)
	}
	defer rows.Close()

	var clientInfoList []ClientInfo
	for rows.Next() {
		var ci ClientInfo
		var wafInstance sql.NullString
		err := rows.Scan(
			&ci.ContainersCount,
			&ci.PTAFConfig,
			&ci.ClientTitle,
			&ci.FluentBitPort,
			&wafInstance,
		)
		if err != nil {
			return nil, fmt.Errorf("ошибка сканирования строки: %w", err)
		}

		if wafInstance.Valid {
			ci.WAFInstance = wafInstance.String
		}

		clientInfoList = append(clientInfoList, ci)
	}

	return clientInfoList, nil
}

// getAppsSettingsByClientTitle получает apps_settings для заданного client_title
func getAppsSettingsByClientTitle(db *sql.DB, clientTitle string) ([]AppsSettings, error) {
	query := `
		SELECT 
			l7resourceid, waf_enabled, waf_vendor,
			custom_input_http_ports, custom_input_https_ports,
			custom_output_http_ports, custom_output_https_ports,
			upstream_angie_custom, server_nginx_custom,
			server_angie_custom, client_title,
			upstream_nginx_custom,
			location_angie_custom, location_nginx_custom,
			server_directives_angie_custom, server_directives_nginx_custom,
			sni,
			mode,
			ssl_enabled,
			custom_angie_ssl,
			max_fails,
			fail_timeout
		FROM apps_settings
		WHERE client_title = $1
		ORDER BY l7resourceid ASC
	`

	rows, err := db.Query(query, clientTitle)
	if err != nil {
		if strings.Contains(err.Error(), "does not exist") {
			return nil, fmt.Errorf("ошибка структуры таблицы apps_settings: %w (возможно изменилась схема БД)", err)
		}
		return nil, fmt.Errorf("ошибка выполнения запроса: %w", err)
	}
	defer rows.Close()

	var appsSettingsList []AppsSettings
	for rows.Next() {
		var a AppsSettings
		err := rows.Scan(
			&a.L7ResourceID, &a.WAFEnabled, &a.WAFVendor,
			&a.CustomInputHTTPPorts, &a.CustomInputHTTPSPorts,
			&a.CustomOutputHTTPPorts, &a.CustomOutputHTTPSPorts,
			&a.UpstreamAngieCustom, &a.ServerNginxCustom,
			&a.ServerAngieCustom, &a.ClientTitle,
			&a.UpstreamNginxCustom,
			&a.LocationAngieCustom, &a.LocationNginxCustom,
			&a.ServerDirectivesAngieCustom, &a.ServerDirectivesNginxCustom,
			&a.SNI,
			&a.Mode,
			&a.SSLEnabled,
			&a.CustomAngieSSL,
			&a.MaxFails,
			&a.FailTimeout,
		)
		if err != nil {
			return nil, fmt.Errorf("ошибка сканирования строки: %w", err)
		}
		appsSettingsList = append(appsSettingsList, a)
	}

	return appsSettingsList, nil
}

// getClientInfoByClientTitle получает информацию о клиенте из таблицы client_info
func getClientInfoByClientTitle(db *sql.DB, clientTitle string) (*ClientInfo, error) {
	query := `
		SELECT containers_count, ptaf_config, client_title, fluent_bit_port, waf_instance
		FROM client_info
		WHERE client_title = $1
	`

	var clientInfo ClientInfo
	var wafInstance sql.NullString
	err := db.QueryRow(query, clientTitle).Scan(
		&clientInfo.ContainersCount,
		&clientInfo.PTAFConfig,
		&clientInfo.ClientTitle,
		&clientInfo.FluentBitPort,
		&wafInstance,
	)
	if err != nil {
		if strings.Contains(err.Error(), "does not exist") {
			return nil, fmt.Errorf("ошибка структуры таблицы client_info: %w (возможно изменилась схема БД)", err)
		}
		return nil, fmt.Errorf("ошибка получения данных из client_info: %w", err)
	}

	if wafInstance.Valid {
		clientInfo.WAFInstance = wafInstance.String
	}

	return &clientInfo, nil
}
