package upstream

import (
	"net/http"
	"sort"

	"github.com/0xJacky/Nginx-UI/internal/upstream"
	"github.com/0xJacky/Nginx-UI/model"
	"github.com/0xJacky/Nginx-UI/query"
	"github.com/gin-gonic/gin"
	"github.com/uozi-tech/cosy"
	"github.com/uozi-tech/cosy/logger"
)

// SocketInfo represents a socket with its configuration and health status
type SocketInfo struct {
	Socket       string           `json:"socket"`        // host:port
	Host         string           `json:"host"`          // hostname/IP
	Port         string           `json:"port"`          // port number
	Type         string           `json:"type"`          // proxy_pass, grpc_pass, or upstream
	IsConsul     bool             `json:"is_consul"`     // whether this is a consul service
	UpstreamName string           `json:"upstream_name"` // which upstream this belongs to (if any)
	LastCheck    string           `json:"last_check"`    // last time health check was performed
	Status       *upstream.Status `json:"status"`        // health check status
	Enabled      bool             `json:"enabled"`       // whether health check is enabled
}

// GetSocketList returns all sockets with their configuration and health status
func GetSocketList(c *gin.Context) {
	service := upstream.GetUpstreamService()

	// Get all target infos
	targets := service.GetTargetInfos()

	// Get availability map
	availabilityMap := service.GetAvailabilityMap()

	// Get all socket configurations from database
	u := query.UpstreamConfig
	configs, err := u.Find()
	if err != nil {
		cosy.ErrHandler(c, err)
		return
	}

	// Create a map for quick lookup of enabled status
	configMap := make(map[string]bool)
	for _, config := range configs {
		configMap[config.Socket] = config.Enabled
	}

	// Build response
	result := make([]SocketInfo, 0, len(targets))
	for _, target := range targets {
		socketAddr := formatSocketAddress(target.Host, target.Port)

		// Get enabled status from database, default to true if not found
		enabled := true
		if val, exists := configMap[socketAddr]; exists {
			enabled = val
		}

		// Get health status
		var status *upstream.Status
		if s, exists := availabilityMap[socketAddr]; exists {
			status = s
		}

		// Find which upstream this belongs to
		upstreamName := findUpstreamForSocket(service, target.ProxyTarget)

		info := SocketInfo{
			Socket:       socketAddr,
			Host:         target.Host,
			Port:         target.Port,
			Type:         target.Type,
			IsConsul:     target.IsConsul,
			UpstreamName: upstreamName,
			LastCheck:    target.LastSeen.Format("2006-01-02 15:04:05"),
			Status:       status,
			Enabled:      enabled,
		}
		result = append(result, info)
	}

	// Sort by socket address for stable ordering
	sort.Slice(result, func(i, j int) bool {
		return result[i].Socket < result[j].Socket
	})

	c.JSON(http.StatusOK, gin.H{
		"data": result,
	})
}

// UpdateSocketConfigRequest represents the request body for updating socket config
type UpdateSocketConfigRequest struct {
	Enabled bool `json:"enabled"`
}

// UpdateSocketConfig updates the enabled status of a socket
func UpdateSocketConfig(c *gin.Context) {
	socket := c.Param("socket")

	var req UpdateSocketConfigRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		cosy.ErrHandler(c, err)
		return
	}

	u := query.UpstreamConfig

	// Check if config exists
	config, err := u.Where(u.Socket.Eq(socket)).First()
	if err != nil {
		// Create new config if not found
		config = &model.UpstreamConfig{
			Socket:  socket,
			Enabled: req.Enabled,
		}
		if err := u.Create(config); err != nil {
			logger.Error("Failed to create socket config:", err)
			cosy.ErrHandler(c, err)
			return
		}
		// Invalidate cache after creating new config
		service := upstream.GetUpstreamService()
		service.InvalidateDisabledSocketsCache()
	} else {
		// Update existing config
		if _, err := u.Where(u.Socket.Eq(socket)).Update(u.Enabled, req.Enabled); err != nil {
			logger.Error("Failed to update socket config:", err)
			cosy.ErrHandler(c, err)
			return
		}
	}

	// Invalidate the disabled sockets cache to ensure changes take effect immediately
	service := upstream.GetUpstreamService()
	service.InvalidateDisabledSocketsCache()

	c.JSON(http.StatusOK, gin.H{
		"message": "Socket config updated successfully",
	})
}

// findUpstreamForSocket finds which upstream a socket belongs to
func findUpstreamForSocket(service *upstream.Service, target upstream.ProxyTarget) string {
	socketAddr := formatSocketAddress(target.Host, target.Port)
	upstreams := service.GetAllUpstreamDefinitions()

	for name, upstream := range upstreams {
		for _, server := range upstream.Servers {
			serverAddr := formatSocketAddress(server.Host, server.Port)
			if serverAddr == socketAddr {
				return name
			}
		}
	}
	return ""
}

