package api

import (
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"account/internal/agentproto"
	"account/internal/agentserver"
	"account/internal/xrayconfig"
)

const agentIDHeader = "X-Agent-ID"

func (h *handler) listAgentUsers(c *gin.Context) {
	if h.agentRegistry == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "agent_registry_unavailable"})
		return
	}

	token := extractToken(c.GetHeader("Authorization"))
	if token == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "missing_token"})
		return
	}

	credIdentity, ok := h.agentRegistry.Authenticate(token)
	if !ok || credIdentity == nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid_token"})
		return
	}

	agentID := strings.TrimSpace(c.GetHeader(agentIDHeader))
	if agentID == "" {
		agentID = strings.TrimSpace(c.Query("agentId"))
	}
	if agentID == "" {
		agentID = credIdentity.ID
	}

	identity := *credIdentity
	if agentID != "" && agentID != identity.ID {
		// Shared token scenario: register a concrete agent id so sandbox bindings can target it.
		identity = h.agentRegistry.RegisterAgent(agentID, identity.Groups)
	}

	now := time.Now().UTC()
	clients := make([]xrayconfig.Client, 0, 16)

	users, err := h.store.ListUsers(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "list_users_failed"})
		return
	}

	for _, u := range users {
		if !u.Active {
			continue
		}
		email := strings.ToLower(strings.TrimSpace(u.Email))

		// Sandbox is a special demo identity with a rotating proxy UUID.
		// Always include it (and rotate on read if needed), so every node/region
		// can sync a consistent sandbox client for the Guest experience.
		if email == sandboxUserEmail {
			sandboxUser := u
			_ = h.ensureSandboxProxyUUID(c.Request.Context(), &sandboxUser)

			id := strings.TrimSpace(sandboxUser.ProxyUUID)
			if id == "" {
				id = strings.TrimSpace(sandboxUser.ID)
			}
			if id != "" {
				clients = append(clients, xrayconfig.Client{
					ID:    id,
					Email: strings.TrimSpace(sandboxUser.Email),
					Flow:  xrayconfig.DefaultFlow,
				})
			}
			continue
		}

		id := strings.TrimSpace(u.ProxyUUID)
		if id == "" {
			id = strings.TrimSpace(u.ID)
		}
		if id == "" {
			continue
		}
		clients = append(clients, xrayconfig.Client{
			ID:    id,
			Email: strings.TrimSpace(u.Email),
			Flow:  xrayconfig.DefaultFlow,
		})
	}

	c.JSON(http.StatusOK, agentproto.ClientListResponse{
		Clients:     clients,
		Total:       len(clients),
		GeneratedAt: now,
	})
}

func (h *handler) reportAgentStatus(c *gin.Context) {
	if h.agentRegistry == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "agent_registry_unavailable"})
		return
	}

	token := extractToken(c.GetHeader("Authorization"))
	if token == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "missing_token"})
		return
	}

	credIdentity, ok := h.agentRegistry.Authenticate(token)
	if !ok || credIdentity == nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid_token"})
		return
	}

	var report agentproto.StatusReport
	if err := c.ShouldBindJSON(&report); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_request", "message": err.Error()})
		return
	}

	agentID := strings.TrimSpace(report.AgentID)
	if agentID == "" {
		agentID = strings.TrimSpace(c.GetHeader(agentIDHeader))
	}
	if agentID == "" {
		agentID = credIdentity.ID
	}

	identity := *credIdentity
	if agentID != "" && agentID != identity.ID {
		identity = h.agentRegistry.RegisterAgent(agentID, identity.Groups)
	}

	// Ensure report uses the resolved agent id.
	report.AgentID = identity.ID
	h.agentRegistry.ReportStatus(identity, report)

	c.Status(http.StatusNoContent)
}

var _ = agentserver.Identity{}
