package handler

import (
	"encoding/json"
	"errors"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
	"xboard-go/internal/database"
	"xboard-go/internal/model"
	"xboard-go/internal/service"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

func AdminServerGroupsFetch(c *gin.Context) {
	var groups []model.ServerGroup
	if err := database.DB.Order("id DESC").Find(&groups).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to fetch server groups")
		return
	}

	var servers []model.Server
	if err := database.DB.Find(&servers).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to fetch servers")
		return
	}

	type usageCount struct {
		GroupID *int
		Total   int64
	}
	var userCounts []usageCount
	if err := database.DB.Model(&model.User{}).
		Select("group_id, COUNT(*) AS total").
		Group("group_id").
		Scan(&userCounts).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to fetch group usage")
		return
	}

	userCountMap := make(map[int]int64, len(userCounts))
	for _, item := range userCounts {
		if item.GroupID == nil {
			continue
		}
		userCountMap[*item.GroupID] = item.Total
	}

	serverCountMap := make(map[int]int)
	for _, server := range servers {
		for _, groupID := range decodeIntSlice(server.GroupIDs) {
			serverCountMap[groupID] += 1
		}
	}

	result := make([]gin.H, 0, len(groups))
	for _, group := range groups {
		result = append(result, gin.H{
			"id":           group.ID,
			"name":         group.Name,
			"created_at":   group.CreatedAt,
			"updated_at":   group.UpdatedAt,
			"user_count":   userCountMap[group.ID],
			"server_count": serverCountMap[group.ID],
		})
	}

	Success(c, result)
}

func AdminServerGroupSave(c *gin.Context) {
	var payload struct {
		ID   *int   `json:"id"`
		Name string `json:"name"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, http.StatusBadRequest, "invalid request body")
		return
	}

	name := strings.TrimSpace(payload.Name)
	if name == "" {
		Fail(c, http.StatusUnprocessableEntity, "group name is required")
		return
	}

	now := time.Now().Unix()
	if payload.ID != nil && *payload.ID > 0 {
		if err := database.DB.Model(&model.ServerGroup{}).
			Where("id = ?", *payload.ID).
			Updates(map[string]any{
				"name":       name,
				"updated_at": now,
			}).Error; err != nil {
			Fail(c, http.StatusInternalServerError, "failed to save server group")
			return
		}
		Success(c, true)
		return
	}

	group := model.ServerGroup{
		Name:      name,
		CreatedAt: now,
		UpdatedAt: now,
	}
	if err := database.DB.Create(&group).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to create server group")
		return
	}

	Success(c, true)
}

func AdminServerGroupDrop(c *gin.Context) {
	var payload struct {
		ID int `json:"id"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil || payload.ID <= 0 {
		Fail(c, http.StatusBadRequest, "group id is required")
		return
	}

	var group model.ServerGroup
	if err := database.DB.Where("id = ?", payload.ID).First(&group).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			Fail(c, http.StatusBadRequest, "server group does not exist")
			return
		}
		Fail(c, http.StatusInternalServerError, "failed to load server group")
		return
	}

	var servers []model.Server
	if err := database.DB.Find(&servers).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to inspect server usage")
		return
	}
	for _, server := range servers {
		if intSliceContains(decodeIntSlice(server.GroupIDs), payload.ID) {
			Fail(c, http.StatusBadRequest, "server group is still used by nodes")
			return
		}
	}

	var planUsage int64
	if err := database.DB.Model(&model.Plan{}).Where("group_id = ?", payload.ID).Count(&planUsage).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to inspect plan usage")
		return
	}
	if planUsage > 0 {
		Fail(c, http.StatusBadRequest, "server group is still used by plans")
		return
	}

	var userUsage int64
	if err := database.DB.Model(&model.User{}).Where("group_id = ?", payload.ID).Count(&userUsage).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to inspect user usage")
		return
	}
	if userUsage > 0 {
		Fail(c, http.StatusBadRequest, "server group is still used by users")
		return
	}

	if err := database.DB.Delete(&group).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to delete server group")
		return
	}

	Success(c, true)
}

func AdminServerRoutesFetch(c *gin.Context) {
	var routes []model.ServerRoute
	if err := database.DB.Order("id DESC").Find(&routes).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to fetch server routes")
		return
	}

	result := make([]gin.H, 0, len(routes))
	for _, route := range routes {
		result = append(result, gin.H{
			"id":           route.ID,
			"remarks":      stringValue(route.Remarks),
			"match":        decodeStringSlice(route.Match),
			"action":       stringValue(route.Action),
			"action_value": stringValue(route.ActionValue),
			"created_at":   route.CreatedAt,
			"updated_at":   route.UpdatedAt,
		})
	}

	Success(c, result)
}

func AdminServerRouteSave(c *gin.Context) {
	var payload struct {
		ID          *int  `json:"id"`
		Remarks     string `json:"remarks"`
		Match       []any  `json:"match"`
		Action      string `json:"action"`
		ActionValue any    `json:"action_value"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, http.StatusBadRequest, "invalid request body")
		return
	}

	remarks := strings.TrimSpace(payload.Remarks)
	if remarks == "" {
		Fail(c, http.StatusUnprocessableEntity, "remarks is required")
		return
	}
	match := filterNonEmptyStrings(payload.Match)
	if len(match) == 0 {
		Fail(c, http.StatusUnprocessableEntity, "match is required")
		return
	}
	action := strings.TrimSpace(payload.Action)
	if !isAllowedRouteAction(action) {
		Fail(c, http.StatusUnprocessableEntity, "invalid route action")
		return
	}

	matchJSON, err := marshalJSON(match, false)
	if err != nil {
		Fail(c, http.StatusBadRequest, "invalid route match")
		return
	}

	now := time.Now().Unix()
	values := map[string]any{
		"remarks":      remarks,
		"match":        matchJSON,
		"action":       action,
		"action_value": nullableString(payload.ActionValue),
		"updated_at":   now,
	}

	if payload.ID != nil && *payload.ID > 0 {
		if err := database.DB.Model(&model.ServerRoute{}).Where("id = ?", *payload.ID).Updates(values).Error; err != nil {
			Fail(c, http.StatusInternalServerError, "failed to save server route")
			return
		}
		Success(c, true)
		return
	}

	values["created_at"] = now
	if err := database.DB.Model(&model.ServerRoute{}).Create(values).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to create server route")
		return
	}

	Success(c, true)
}

func AdminServerRouteDrop(c *gin.Context) {
	var payload struct {
		ID int `json:"id"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil || payload.ID <= 0 {
		Fail(c, http.StatusBadRequest, "route id is required")
		return
	}

	result := database.DB.Where("id = ?", payload.ID).Delete(&model.ServerRoute{})
	if result.Error != nil {
		Fail(c, http.StatusInternalServerError, "failed to delete server route")
		return
	}
	if result.RowsAffected == 0 {
		Fail(c, http.StatusBadRequest, "server route does not exist")
		return
	}

	Success(c, true)
}

func AdminServerManageGetNodes(c *gin.Context) {
	var servers []model.Server
	if err := database.DB.Order("sort ASC, id ASC").Find(&servers).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to fetch nodes")
		return
	}

	var groups []model.ServerGroup
	if err := database.DB.Find(&groups).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to fetch node groups")
		return
	}
	groupMap := make(map[int]model.ServerGroup, len(groups))
	for _, group := range groups {
		groupMap[group.ID] = group
	}

	serverMap := make(map[int]model.Server, len(servers))
	for _, server := range servers {
		serverMap[server.ID] = server
	}

	result := make([]gin.H, 0, len(servers))
	for _, server := range servers {
		result = append(result, serializeAdminServer(server, groupMap, serverMap))
	}

	Success(c, result)
}

func AdminServerManageSort(c *gin.Context) {
	var payload []struct {
		ID    int `json:"id"`
		Order int `json:"order"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, http.StatusBadRequest, "invalid request body")
		return
	}

	if err := database.DB.Transaction(func(tx *gorm.DB) error {
		for _, item := range payload {
			if item.ID <= 0 {
				continue
			}
			if err := tx.Model(&model.Server{}).Where("id = ?", item.ID).Update("sort", item.Order).Error; err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		Fail(c, http.StatusInternalServerError, "failed to sort nodes")
		return
	}

	Success(c, true)
}

func AdminServerManageSave(c *gin.Context) {
	payload := map[string]any{}
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, http.StatusBadRequest, "invalid request body")
		return
	}

	values, id, err := normalizeServerPayload(payload)
	if err != nil {
		Fail(c, http.StatusBadRequest, err.Error())
		return
	}

	if id > 0 {
		result := database.DB.Model(&model.Server{}).Where("id = ?", id).Updates(values)
		if result.Error != nil {
			Fail(c, http.StatusInternalServerError, "failed to save node")
			return
		}
		if result.RowsAffected == 0 {
			Fail(c, http.StatusBadRequest, "node does not exist")
			return
		}
		Success(c, true)
		return
	}

	if err := database.DB.Model(&model.Server{}).Create(values).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to create node")
		return
	}

	Success(c, true)
}

func AdminServerManageUpdate(c *gin.Context) {
	var payload struct {
		ID   int `json:"id"`
		Show *int `json:"show"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil || payload.ID <= 0 {
		Fail(c, http.StatusBadRequest, "invalid request body")
		return
	}

	updates := map[string]any{}
	if payload.Show != nil {
		updates["show"] = *payload.Show != 0
	}
	if len(updates) == 0 {
		Fail(c, http.StatusBadRequest, "no updates were provided")
		return
	}

	result := database.DB.Model(&model.Server{}).Where("id = ?", payload.ID).Updates(updates)
	if result.Error != nil {
		Fail(c, http.StatusInternalServerError, "failed to update node")
		return
	}
	if result.RowsAffected == 0 {
		Fail(c, http.StatusBadRequest, "node does not exist")
		return
	}

	Success(c, true)
}

func AdminServerManageDrop(c *gin.Context) {
	var payload struct {
		ID int `json:"id"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil || payload.ID <= 0 {
		Fail(c, http.StatusBadRequest, "node id is required")
		return
	}

	result := database.DB.Where("id = ?", payload.ID).Delete(&model.Server{})
	if result.Error != nil {
		Fail(c, http.StatusInternalServerError, "failed to delete node")
		return
	}
	if result.RowsAffected == 0 {
		Fail(c, http.StatusBadRequest, "node does not exist")
		return
	}

	Success(c, true)
}

func AdminServerManageCopy(c *gin.Context) {
	var payload struct {
		ID int `json:"id"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil || payload.ID <= 0 {
		Fail(c, http.StatusBadRequest, "node id is required")
		return
	}

	var server model.Server
	if err := database.DB.Where("id = ?", payload.ID).First(&server).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			Fail(c, http.StatusBadRequest, "node does not exist")
			return
		}
		Fail(c, http.StatusInternalServerError, "failed to load node")
		return
	}

	now := time.Now()
	server.ID = 0
	server.Code = nil
	server.Show = false
	server.U = 0
	server.D = 0
	server.CreatedAt = &now
	server.UpdatedAt = &now

	if err := database.DB.Create(&server).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to copy node")
		return
	}

	Success(c, true)
}

func AdminServerManageBatchDelete(c *gin.Context) {
	var payload struct {
		IDs []int `json:"ids"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, http.StatusBadRequest, "invalid request body")
		return
	}

	ids := sanitizePositiveIDs(payload.IDs)
	if len(ids) == 0 {
		Fail(c, http.StatusBadRequest, "ids is required")
		return
	}

	if err := database.DB.Where("id IN ?", ids).Delete(&model.Server{}).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to batch delete nodes")
		return
	}

	Success(c, true)
}

func AdminServerManageResetTraffic(c *gin.Context) {
	var payload struct {
		ID int `json:"id"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil || payload.ID <= 0 {
		Fail(c, http.StatusBadRequest, "node id is required")
		return
	}

	result := database.DB.Model(&model.Server{}).Where("id = ?", payload.ID).Updates(map[string]any{
		"u": 0,
		"d": 0,
	})
	if result.Error != nil {
		Fail(c, http.StatusInternalServerError, "failed to reset node traffic")
		return
	}
	if result.RowsAffected == 0 {
		Fail(c, http.StatusBadRequest, "node does not exist")
		return
	}

	Success(c, true)
}

func AdminServerManageBatchResetTraffic(c *gin.Context) {
	var payload struct {
		IDs []int `json:"ids"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, http.StatusBadRequest, "invalid request body")
		return
	}

	ids := sanitizePositiveIDs(payload.IDs)
	if len(ids) == 0 {
		Fail(c, http.StatusBadRequest, "ids is required")
		return
	}

	if err := database.DB.Model(&model.Server{}).Where("id IN ?", ids).Updates(map[string]any{
		"u": 0,
		"d": 0,
	}).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to batch reset node traffic")
		return
	}

	Success(c, true)
}

func serializeAdminServer(server model.Server, groups map[int]model.ServerGroup, servers map[int]model.Server) gin.H {
	groupIDs := decodeIntSlice(server.GroupIDs)
	groupList := make([]gin.H, 0, len(groupIDs))
	for _, groupID := range groupIDs {
		group, ok := groups[groupID]
		if !ok {
			continue
		}
		groupList = append(groupList, gin.H{
			"id":   group.ID,
			"name": group.Name,
		})
	}

	var parent any
	if server.ParentID != nil {
		if parentServer, ok := servers[*server.ParentID]; ok {
			parent = gin.H{
				"id":          parentServer.ID,
				"type":        parentServer.Type,
				"name":        parentServer.Name,
				"host":        parentServer.Host,
				"port":        parentServer.Port,
				"server_port": parentServer.ServerPort,
			}
		}
	}

	lastCheckAt, _ := database.CacheGetJSON[int64](nodeLastCheckKey(&server))
	lastPushAt, _ := database.CacheGetJSON[int64](nodeLastPushKey(&server))
	online, _ := database.CacheGetJSON[int](nodeOnlineKey(&server))
	loadStatus, _ := database.CacheGetJSON[map[string]any](nodeLoadStatusKey(&server))
	metrics, _ := database.CacheGetJSON[map[string]any](nodeMetricsKey(&server))

	isOnline := 0
	now := time.Now().Unix()
	if lastCheckAt > 0 && now-lastCheckAt <= 300 {
		isOnline = 1
	}
	if lastPushAt > 0 && now-lastPushAt <= 300 {
		isOnline = 2
	}

	availableStatus := "offline"
	if isOnline == 1 {
		availableStatus = "online_no_push"
	} else if isOnline == 2 {
		availableStatus = "online"
	}

	return gin.H{
		"id":                server.ID,
		"type":              server.Type,
		"code":              stringValue(server.Code),
		"parent_id":         intValue(server.ParentID),
		"group_ids":         groupIDs,
		"route_ids":         decodeIntSlice(server.RouteIDs),
		"name":              server.Name,
		"rate":              server.Rate,
		"transfer_enable":   int64Value(server.TransferEnable),
		"u":                 server.U,
		"d":                 server.D,
		"tags":              decodeStringSlice(server.Tags),
		"host":              server.Host,
		"port":              server.Port,
		"server_port":       server.ServerPort,
		"protocol_settings": decodeJSON(server.ProtocolSettings),
		"custom_outbounds":  decodeJSON(server.CustomOutbounds),
		"custom_routes":     decodeJSON(server.CustomRoutes),
		"cert_config":       decodeJSON(server.CertConfig),
		"show":              server.Show,
		"sort":              intValue(server.Sort),
		"rate_time_enable":  server.RateTimeEnable,
		"rate_time_ranges":  decodeJSON(server.RateTimeRanges),
		"created_at":        formatTimeValue(server.CreatedAt),
		"updated_at":        formatTimeValue(server.UpdatedAt),
		"groups":            groupList,
		"parent":            parent,
		"last_check_at":     lastCheckAt,
		"last_push_at":      lastPushAt,
		"online":            online,
		"is_online":         isOnline,
		"available_status":  availableStatus,
		"load_status":       loadStatus,
		"metrics":           metrics,
	}
}

func normalizeServerPayload(payload map[string]any) (map[string]any, int, error) {
	id := intFromAny(payload["id"])
	serverType := service.NormalizeServerType(stringFromAny(payload["type"]))
	if serverType == "" {
		return nil, 0, errors.New("node type is required")
	}
	if !service.IsValidServerType(serverType) {
		return nil, 0, errors.New("invalid node type")
	}

	name := strings.TrimSpace(stringFromAny(payload["name"]))
	if name == "" {
		return nil, 0, errors.New("node name is required")
	}
	host := strings.TrimSpace(stringFromAny(payload["host"]))
	if host == "" {
		return nil, 0, errors.New("node host is required")
	}

	port := strings.TrimSpace(stringFromAny(payload["port"]))
	if port == "" {
		return nil, 0, errors.New("node port is required")
	}

	serverPort := intFromAny(payload["server_port"])
	if serverPort <= 0 {
		return nil, 0, errors.New("server_port is required")
	}

	rate, ok := float32FromAny(payload["rate"])
	if !ok {
		return nil, 0, errors.New("rate is required")
	}

	groupIDs, err := marshalJSON(payload["group_ids"], true)
	if err != nil {
		return nil, 0, errors.New("invalid group_ids")
	}
	routeIDs, err := marshalJSON(payload["route_ids"], true)
	if err != nil {
		return nil, 0, errors.New("invalid route_ids")
	}
	tags, err := marshalJSON(payload["tags"], true)
	if err != nil {
		return nil, 0, errors.New("invalid tags")
	}
	protocolSettings, err := marshalJSON(payload["protocol_settings"], false)
	if err != nil {
		return nil, 0, errors.New("invalid protocol_settings")
	}
	rateTimeRanges, err := marshalJSON(payload["rate_time_ranges"], false)
	if err != nil {
		return nil, 0, errors.New("invalid rate_time_ranges")
	}
	customOutbounds, err := marshalJSON(payload["custom_outbounds"], false)
	if err != nil {
		return nil, 0, errors.New("invalid custom_outbounds")
	}
	customRoutes, err := marshalJSON(payload["custom_routes"], false)
	if err != nil {
		return nil, 0, errors.New("invalid custom_routes")
	}
	certConfig, err := marshalJSON(payload["cert_config"], false)
	if err != nil {
		return nil, 0, errors.New("invalid cert_config")
	}

	values := map[string]any{
		"type":              serverType,
		"code":              nullableString(payload["code"]),
		"parent_id":         nullableInt(payload["parent_id"]),
		"group_ids":         groupIDs,
		"route_ids":         routeIDs,
		"name":              name,
		"rate":              rate,
		"transfer_enable":   nullableInt64(payload["transfer_enable"]),
		"tags":              tags,
		"host":              host,
		"port":              port,
		"server_port":       serverPort,
		"protocol_settings": protocolSettings,
		"custom_outbounds":  customOutbounds,
		"custom_routes":     customRoutes,
		"cert_config":       certConfig,
		"show":              boolFromAny(payload["show"]),
		"sort":              nullableInt(payload["sort"]),
		"rate_time_enable":  boolFromAny(payload["rate_time_enable"]),
		"rate_time_ranges":  rateTimeRanges,
	}

	return values, id, nil
}

func decodeJSON(raw *string) any {
	if raw == nil || strings.TrimSpace(*raw) == "" {
		return nil
	}

	var value any
	if err := json.Unmarshal([]byte(*raw), &value); err != nil {
		return *raw
	}
	return value
}

func decodeIntSlice(raw *string) []int {
	if raw == nil || strings.TrimSpace(*raw) == "" {
		return []int{}
	}

	var value []any
	if err := json.Unmarshal([]byte(*raw), &value); err == nil {
		result := make([]int, 0, len(value))
		for _, item := range value {
			if parsed := intFromAny(item); parsed > 0 {
				result = append(result, parsed)
			}
		}
		return result
	}

	parts := strings.Split(*raw, ",")
	result := make([]int, 0, len(parts))
	for _, part := range parts {
		if parsed, err := strconv.Atoi(strings.TrimSpace(part)); err == nil && parsed > 0 {
			result = append(result, parsed)
		}
	}
	return result
}

func decodeStringSlice(raw *string) []string {
	if raw == nil || strings.TrimSpace(*raw) == "" {
		return []string{}
	}

	var value []any
	if err := json.Unmarshal([]byte(*raw), &value); err == nil {
		return filterNonEmptyStrings(value)
	}

	return filterNonEmptyStrings(strings.Split(*raw, ","))
}

func marshalJSON(value any, fallbackEmptyArray bool) (*string, error) {
	if value == nil {
		if fallbackEmptyArray {
			empty := "[]"
			return &empty, nil
		}
		return nil, nil
	}

	if text, ok := value.(string); ok {
		text = strings.TrimSpace(text)
		if text == "" {
			if fallbackEmptyArray {
				empty := "[]"
				return &empty, nil
			}
			return nil, nil
		}
		if json.Valid([]byte(text)) {
			return &text, nil
		}
	}

	payload, err := json.Marshal(value)
	if err != nil {
		return nil, err
	}
	text := string(payload)
	return &text, nil
}

func stringFromAny(value any) string {
	switch typed := value.(type) {
	case string:
		return typed
	case json.Number:
		return typed.String()
	case float64:
		return strconv.FormatFloat(typed, 'f', -1, 64)
	case float32:
		return strconv.FormatFloat(float64(typed), 'f', -1, 32)
	case int:
		return strconv.Itoa(typed)
	case int64:
		return strconv.FormatInt(typed, 10)
	case bool:
		if typed {
			return "true"
		}
		return "false"
	default:
		return ""
	}
}

func nullableString(value any) any {
	text := strings.TrimSpace(stringFromAny(value))
	if text == "" {
		return nil
	}
	return text
}

func intFromAny(value any) int {
	switch typed := value.(type) {
	case int:
		return typed
	case int8:
		return int(typed)
	case int16:
		return int(typed)
	case int32:
		return int(typed)
	case int64:
		return int(typed)
	case float32:
		return int(typed)
	case float64:
		return int(typed)
	case json.Number:
		parsed, _ := typed.Int64()
		return int(parsed)
	case string:
		parsed, _ := strconv.Atoi(strings.TrimSpace(typed))
		return parsed
	default:
		return 0
	}
}

func float32FromAny(value any) (float32, bool) {
	switch typed := value.(type) {
	case float32:
		return typed, true
	case float64:
		return float32(typed), true
	case int:
		return float32(typed), true
	case int64:
		return float32(typed), true
	case json.Number:
		parsed, err := typed.Float64()
		return float32(parsed), err == nil
	case string:
		parsed, err := strconv.ParseFloat(strings.TrimSpace(typed), 32)
		return float32(parsed), err == nil
	default:
		return 0, false
	}
}

func nullableInt(value any) any {
	parsed := intFromAny(value)
	if parsed <= 0 {
		return nil
	}
	return parsed
}

func nullableInt64(value any) any {
	switch typed := value.(type) {
	case int64:
		if typed > 0 {
			return typed
		}
	case int:
		if typed > 0 {
			return int64(typed)
		}
	case float64:
		if typed > 0 {
			return int64(typed)
		}
	case string:
		if parsed, err := strconv.ParseInt(strings.TrimSpace(typed), 10, 64); err == nil && parsed > 0 {
			return parsed
		}
	}
	return nil
}

func boolFromAny(value any) bool {
	switch typed := value.(type) {
	case bool:
		return typed
	case int:
		return typed != 0
	case int64:
		return typed != 0
	case float64:
		return typed != 0
	case string:
		text := strings.TrimSpace(strings.ToLower(typed))
		return text == "1" || text == "true" || text == "yes" || text == "on"
	default:
		return false
	}
}

func stringValue(value *string) string {
	if value == nil {
		return ""
	}
	return *value
}

func intValue(value *int) any {
	if value == nil {
		return nil
	}
	return *value
}

func int64Value(value *int64) any {
	if value == nil {
		return nil
	}
	return *value
}

func filterNonEmptyStrings(values any) []string {
	result := make([]string, 0)
	switch typed := values.(type) {
	case []string:
		for _, item := range typed {
			item = strings.TrimSpace(item)
			if item != "" {
				result = append(result, item)
			}
		}
	case []any:
		for _, item := range typed {
			text := strings.TrimSpace(stringFromAny(item))
			if text != "" {
				result = append(result, text)
			}
		}
	}
	sort.Strings(result)
	return result
}

func sanitizePositiveIDs(ids []int) []int {
	unique := make(map[int]struct{}, len(ids))
	result := make([]int, 0, len(ids))
	for _, id := range ids {
		if id <= 0 {
			continue
		}
		if _, exists := unique[id]; exists {
			continue
		}
		unique[id] = struct{}{}
		result = append(result, id)
	}
	sort.Ints(result)
	return result
}

func intSliceContains(values []int, target int) bool {
	for _, value := range values {
		if value == target {
			return true
		}
	}
	return false
}

func isAllowedRouteAction(action string) bool {
	switch action {
	case "block", "direct", "dns", "proxy":
		return true
	default:
		return false
	}
}
