package system

import (
	"errors"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/desktop/internal/metrics"
	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
)

// errInvalidTimeWindow is returned by ParseTimeWindowString when the input
// does not match a supported window label.
var errInvalidTimeWindow = errors.New("invalid time window, must be one of: 6h, 12h, 1d, 3d, 1w")

var errInvalidTokenMetric = errors.New("invalid token metric, must be one of: input, cached, output")

// NewServerMessageStatsHandler creates a handler that returns server message statistics.
func NewServerMessageStatsHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		collector := metrics.DefaultServerMessageCollector()
		if collector == nil {
			c.JSON(http.StatusOK, []metrics.ServerMessageSummary{})
			return
		}
		summary := collector.Summary()
		if summary == nil {
			summary = []metrics.ServerMessageSummary{}
		}
		for i := range summary {
			if ch, ok := profile.Default().Servers().Get(summary[i].ServerID); ok {
				summary[i].ServerName = ch.Name
			}
		}
		c.JSON(http.StatusOK, summary)
	}
}

// NewServerMessageTimeSeriesHandler creates a handler that returns per-server message time-series data.
// Query parameter "window" specifies the time window (6h, 12h, 1d, 3d, 1w). Defaults to 12h.
func NewServerMessageTimeSeriesHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}

		collector := metrics.DefaultServerMessageCollector()
		if collector == nil {
			c.JSON(http.StatusOK, metrics.ServerMessageTimeSeriesResponse{
				Interval: int(metrics.TimeSeriesAggregationInterval(timeWindow).Seconds()),
				Data:     []metrics.ServerMessageTimeSeriesPoint{},
			})
			return
		}

		result := collector.QueryTimeSeries(timeWindow)
		c.JSON(http.StatusOK, result)
	}
}

// NewServerMessageDirectionalTimeSeriesHandler creates a handler that returns
// aggregated received/sent message time-series data across all server connections.
// Query parameter "window" specifies the time window (6h, 12h, 1d, 3d, 1w). Defaults to 12h.
func NewServerMessageDirectionalTimeSeriesHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}

		collector := metrics.DefaultServerMessageCollector()
		if collector == nil {
			c.JSON(http.StatusOK, metrics.ServerMessageDirectionalTimeSeriesResponse{
				Interval: int(metrics.TimeSeriesAggregationInterval(timeWindow).Seconds()),
				Data:     []metrics.ServerMessageDirectionalTimeSeriesPoint{},
			})
			return
		}

		result := collector.QueryDirectionalTimeSeries(timeWindow)
		c.JSON(http.StatusOK, result)
	}
}

// NewServerMessageWindowSummaryHandler creates a handler that returns per-server
// received/sent totals within the given time window.
// Query parameter "window" specifies the time window (6h, 12h, 1d, 3d, 1w). Defaults to 12h.
func NewServerMessageWindowSummaryHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		collector := metrics.DefaultServerMessageCollector()
		if collector == nil {
			c.JSON(http.StatusOK, []metrics.ServerMessageWindowSummaryItem{})
			return
		}

		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}

		summary := collector.QueryWindowSummary(timeWindow)
		if summary == nil {
			summary = []metrics.ServerMessageWindowSummaryItem{}
		}
		for i := range summary {
			if ch, ok := profile.Default().Servers().Get(summary[i].ServerID); ok {
				summary[i].ServerName = ch.Name
			}
		}
		c.JSON(http.StatusOK, summary)
	}
}

// NewAgentMessageBarSeriesHandler creates a handler that returns one agent's
// inbound/outbound message counts in bar-chart buckets.
func NewAgentMessageBarSeriesHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}

		agentID := c.Param("agentId")
		c.JSON(http.StatusOK, BuildAgentMessageBarSeries(agentID, timeWindow))
	}
}

// NewAgentMessageSummaryHandler creates a handler that returns one agent's
// inbound/outbound message totals within the requested time window.
func NewAgentMessageSummaryHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}

		agentID := c.Param("agentId")
		c.JSON(http.StatusOK, BuildAgentMessageSummary(agentID, timeWindow))
	}
}

// NewAgentMessageDirectionalTimeSeriesHandler creates a handler that returns
// one agent's inbound/outbound message trend within the requested time window.
func NewAgentMessageDirectionalTimeSeriesHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}

		agentID := c.Param("agentId")
		c.JSON(http.StatusOK, BuildAgentMessageDirectionalTimeSeries(agentID, timeWindow))
	}
}

// NewAgentTokenSummaryHandler creates a handler that returns one agent's
// token usage totals within the requested time window.
func NewAgentTokenSummaryHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}

		agentID := c.Param("agentId")
		c.JSON(http.StatusOK, BuildAgentTokenSummary(agentID, timeWindow))
	}
}

// NewAgentTokenTimeSeriesHandler creates a handler that returns one agent's
// aggregate token usage trend within the requested time window.
func NewAgentTokenTimeSeriesHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}

		agentID := c.Param("agentId")
		c.JSON(http.StatusOK, BuildAgentTokenTimeSeries(agentID, timeWindow))
	}
}

// NewAgentModelTokenBarSeriesHandler creates a handler that returns one
// agent's token usage grouped by model for a single token metric.
func NewAgentModelTokenBarSeriesHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		timeWindow, ok := parseTimeWindow(c)
		if !ok {
			return
		}
		tokenMetric, ok := parseTokenMetric(c)
		if !ok {
			return
		}

		agentID := c.Param("agentId")
		c.JSON(http.StatusOK, BuildAgentModelTokenBarSeries(agentID, timeWindow, tokenMetric))
	}
}

// parseTimeWindow extracts and validates the "window" query parameter.
// Returns the parsed TimeWindow and true on success; writes a 400 error and returns false on failure.
func parseTimeWindow(c *gin.Context) (metrics.TimeWindow, bool) {
	tw, err := ParseTimeWindowString(c.DefaultQuery("window", "12h"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return 0, false
	}
	return tw, true
}

func parseTokenMetric(c *gin.Context) (string, bool) {
	metric := c.DefaultQuery("metric", "input")
	switch metric {
	case "input", "cached", "output":
		return metric, true
	default:
		c.JSON(http.StatusBadRequest, gin.H{"error": errInvalidTokenMetric.Error()})
		return "", false
	}
}

// ParseTimeWindowString converts a "6h|12h|1d|3d|1w" string into a TimeWindow.
func ParseTimeWindowString(windowParam string) (metrics.TimeWindow, error) {
	switch windowParam {
	case "6h":
		return metrics.TimeWindow6Hours, nil
	case "12h":
		return metrics.TimeWindow12Hours, nil
	case "1d":
		return metrics.TimeWindow1Day, nil
	case "3d":
		return metrics.TimeWindow3Days, nil
	case "1w":
		return metrics.TimeWindow1Week, nil
	default:
		return 0, errInvalidTimeWindow
	}
}

// BuildServerMessageDirectionalTimeSeries returns directional (received/sent)
// time-series data for the given window, or a zero-value response when
// the collector is not configured.
func BuildServerMessageDirectionalTimeSeries(window metrics.TimeWindow) metrics.ServerMessageDirectionalTimeSeriesResponse {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return metrics.ServerMessageDirectionalTimeSeriesResponse{
			Interval: int(metrics.TimeSeriesAggregationInterval(window).Seconds()),
			Data:     []metrics.ServerMessageDirectionalTimeSeriesPoint{},
		}
	}
	return collector.QueryDirectionalTimeSeries(window)
}

// BuildServerMessageBarSeries returns server message traffic grouped by server
// ID in coarse buckets for the overview bar chart.
func BuildServerMessageBarSeries(window metrics.TimeWindow) metrics.BarSeriesResponse {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return metrics.BarSeriesResponse{
			Interval: int(metrics.BarAggregationInterval(window).Seconds()),
			Series:   []metrics.BarSeriesEntry{},
			Data:     []metrics.BarSeriesPoint{},
		}
	}
	result := collector.QueryServerBarSeries(window)
	servers := profile.Default().Servers().All()
	serverNames := make(map[string]string, len(servers))
	for _, server := range servers {
		serverNames[server.ID] = server.Name
	}
	for i := range result.Series {
		if name := strings.TrimSpace(serverNames[result.Series[i].Key]); name != "" {
			result.Series[i].Label = name
		}
	}
	return result
}

// BuildAgentMessageBarSeries returns inbound/outbound message counts for one
// agent in coarse buckets for the agent detail page.
func BuildAgentMessageBarSeries(agentID string, window metrics.TimeWindow) metrics.BarSeriesResponse {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return metrics.BarSeriesResponse{
			Interval: int(metrics.BarAggregationInterval(window).Seconds()),
			Series: []metrics.BarSeriesEntry{
				{Key: "inbound", Label: "Inbound"},
				{Key: "outbound", Label: "Outbound"},
			},
			Data: []metrics.BarSeriesPoint{},
		}
	}
	return collector.QueryAgentMessageBarSeries(agentID, window)
}

// BuildAgentMessageSummary returns inbound/outbound message totals for one
// agent in the given window.
func BuildAgentMessageSummary(agentID string, window metrics.TimeWindow) metrics.AgentMessageSummary {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return metrics.AgentMessageSummary{AgentID: agentID}
	}
	return collector.QueryAgentMessageSummary(agentID, window)
}

// BuildAgentMessageDirectionalTimeSeries returns inbound/outbound message
// trends for one agent in the given window.
func BuildAgentMessageDirectionalTimeSeries(agentID string, window metrics.TimeWindow) metrics.ServerMessageDirectionalTimeSeriesResponse {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return metrics.ServerMessageDirectionalTimeSeriesResponse{
			Interval: int(metrics.TimeSeriesAggregationInterval(window).Seconds()),
			Data:     []metrics.ServerMessageDirectionalTimeSeriesPoint{},
		}
	}
	return collector.QueryAgentMessageDirectionalTimeSeries(agentID, window)
}

// BuildAgentTokenSummary returns request and token totals for one agent in the given window.
func BuildAgentTokenSummary(agentID string, window metrics.TimeWindow) metrics.AgentTokenSummary {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return metrics.AgentTokenSummary{AgentID: agentID}
	}
	return collector.QueryAgentTokenSummary(agentID, window)
}

// BuildAgentTokenTimeSeries returns aggregate token trends for one agent in the given window.
func BuildAgentTokenTimeSeries(agentID string, window metrics.TimeWindow) metrics.AgentTokenTimeSeriesResponse {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return metrics.AgentTokenTimeSeriesResponse{
			Interval: int(metrics.TimeSeriesAggregationInterval(window).Seconds()),
			Data:     []metrics.TimeSeriesDataPoint{},
		}
	}
	return collector.QueryAgentTokenTimeSeries(agentID, window)
}

// BuildAgentModelTokenBarSeries returns one token metric grouped by model for one agent.
func BuildAgentModelTokenBarSeries(agentID string, window metrics.TimeWindow, tokenMetric string) metrics.BarSeriesResponse {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return metrics.BarSeriesResponse{
			Interval: int(metrics.BarAggregationInterval(window).Seconds()),
			Series:   []metrics.BarSeriesEntry{},
			Data:     []metrics.BarSeriesPoint{},
		}
	}
	return collector.QueryAgentModelTokenBarSeries(agentID, window, tokenMetric)
}

// BuildServerMessageWindowSummary returns per-server received/sent totals for
// the given window, with server name resolution applied.
func BuildServerMessageWindowSummary(window metrics.TimeWindow) []metrics.ServerMessageWindowSummaryItem {
	collector := metrics.DefaultServerMessageCollector()
	if collector == nil {
		return []metrics.ServerMessageWindowSummaryItem{}
	}
	summary := collector.QueryWindowSummary(window)
	if summary == nil {
		summary = []metrics.ServerMessageWindowSummaryItem{}
	}
	for i := range summary {
		if ch, ok := profile.Default().Servers().Get(summary[i].ServerID); ok {
			summary[i].ServerName = ch.Name
		}
	}
	return summary
}
