package dashboard

// Bundle / aggregate endpoints.
//
// These endpoints exist to reduce HTTP round-trips from the dashboard web UI.
// The page-level CRUD endpoints (/agents, /servers, ...)
// stay unchanged; bundle endpoints are additive and compose existing response
// builders.

import (
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agentview"
	"github.com/tidbcloud/AgentTeam/desktop/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/desktop/internal/api/system"
	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
)

// ---------------------------------------------------------------------------
// GET /bundles/agents
// ---------------------------------------------------------------------------

// NewAgentsBundleHandler returns a handler that combines the agent list into a
// single response used by AgentsPage.
//
// The page previously fired multiple requests every 10 seconds. Active
// sessions are still served by a separate endpoint that consults the
// dispatcher; the static agent config now rides along here.
func NewAgentsBundleHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		mgr := profile.Default()

		agents := mgr.Agents().All()
		agentResp := make([]agentview.Snapshot, 0, len(agents))
		for _, ag := range agents {
			agentResp = append(agentResp, BuildAgentResponse(ag))
		}

		c.JSON(http.StatusOK, dto.AgentsBundleResponse{
			Agents: agentResp,
		})
	}
}

// ---------------------------------------------------------------------------
// GET /bundles/servers
// ---------------------------------------------------------------------------

// NewServersBundleHandler returns a handler that combines AgentTeam server
// configuration + agents + 12h/24h window summaries into a single response
// used by the server connection views.
//
// Rationale: the page needs servers, agents (for binding UI), and per-row
// message stats on mount; this collapses three requests into one. Per-row
// extras such as tool lists are still fetched on demand when a row is
// opened.
func NewServersBundleHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		mgr := profile.Default()

		servers := mgr.Servers().All()
		serverResp := make([]dto.ServerResponse, 0, len(servers))
		for _, server := range servers {
			serverResp = append(serverResp, toServerResponse(server))
		}

		agents := mgr.Agents().All()
		agentResp := make([]agentview.Snapshot, 0, len(agents))
		for _, ag := range agents {
			agentResp = append(agentResp, BuildAgentResponse(ag))
		}

		window12h, _ := system.ParseTimeWindowString("12h")
		window1d, _ := system.ParseTimeWindowString("1d")

		c.JSON(http.StatusOK, dto.ServersBundleResponse{
			Servers:          serverResp,
			Agents:           agentResp,
			WindowSummary12h: system.BuildServerMessageWindowSummary(window12h),
			WindowSummary1d:  system.BuildServerMessageWindowSummary(window1d),
		})
	}
}

// ---------------------------------------------------------------------------
// GET /bundles/runtimes
// ---------------------------------------------------------------------------

// NewRuntimesBundleHandler returns a handler that combines configured runtime
// records with a fresh PATH detection pass. RuntimesPage fetches both on
// mount; this endpoint collapses them.
func NewRuntimesBundleHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		resp, err := LoadRuntimesBundle()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, resp)
	}
}

// LoadRuntimesBundle runs a fresh PATH detection pass, composes it with
// the persisted runtime records and agent list, and returns the full
// bundle used by the Runtimes dashboard page and the
// machine.runtimes.bundle remote endpoint.
func LoadRuntimesBundle() (dto.RuntimesBundleResponse, error) {
	mgr := profile.Default()

	detect := mgr.Runtimes().RefreshDetect()
	detectResp := make([]dto.DetectRuntimeResponse, 0, len(detect))
	for _, d := range detect {
		detectResp = append(detectResp, toDetectRuntimeResponse(d))
	}

	agents := mgr.Agents().All()
	summary := dto.RuntimeBundleSummary{}
	runtimeState := buildDashboardRuntimes(mgr.Runtimes().All(), detect, agents, &summary)
	consoleAgents := buildDashboardAgents(agents, nil, runtimeState, &summary)

	return dto.RuntimesBundleResponse{
		Summary:  summary,
		Runtimes: runtimeState.runtimes,
		Agents:   consoleAgents,
		Detected: detectResp,
	}, nil
}

// ---------------------------------------------------------------------------
// GET /bundles/settings
// ---------------------------------------------------------------------------

// NewSettingsBundleHandler returns a handler that combines persisted settings
// with system info. SettingsPage fires both on mount; this endpoint replaces
// that Promise.all with one request.
func NewSettingsBundleHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		settings, err := LoadSettingsBundle()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, dto.SettingsBundleResponse{
			Settings:   settings,
			SystemInfo: system.BuildSystemInfo(),
		})
	}
}

// ---------------------------------------------------------------------------
// GET /bundles/home?window=…
// ---------------------------------------------------------------------------

// NewHomeBundleHandler returns a handler that composes HomePage's mount-time
// data in a single response: overview, system info, aggregate counts, LLM
// summary + time-series, and server message directional/window summary for
// the selected window.
func NewHomeBundleHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		window, err := system.ParseTimeWindowString(c.DefaultQuery("window", "12h"))
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		overview := BuildOverview()
		resp := dto.HomeBundleResponse{
			Overview:   overview,
			SystemInfo: system.BuildSystemInfo(),
			Counts: dto.HomeCounts{
				Skills: overview.Summary.SkillCount,
			},
			LLMSummary:                 gin.H{"stats": system.BuildLLMStatsSummary()},
			LLMTimeSeries:              system.BuildLLMTimeSeries(window),
			LLMModelTimeSeries:         system.BuildLLMModelTimeSeries(window),
			LLMInputTokenBar:           system.BuildLLMModelTokenBarSeries(window, system.LLMTokenMetricInput),
			LLMCachedTokenBar:          system.BuildLLMModelTokenBarSeries(window, system.LLMTokenMetricCached),
			LLMOutputTokenBar:          system.BuildLLMModelTokenBarSeries(window, system.LLMTokenMetricOutput),
			ServerMessageDirectional:   system.BuildServerMessageDirectionalTimeSeries(window),
			ServerMessageBar:           system.BuildServerMessageBarSeries(window),
			ServerMessageWindowSummary: system.BuildServerMessageWindowSummary(window),
		}
		c.JSON(http.StatusOK, resp)
	}
}

// ---------------------------------------------------------------------------
// GET /bundles/agents/:agentId/stats?window=…
// ---------------------------------------------------------------------------

// NewAgentStatsBundleHandler returns a handler that combines the read-only
// AgentDetailPage stat cards and charts for one selected agent/window. This
// collapses the previous detail view fan-out from eight polling requests to one
// polling request while keeping workspace and activity as lazy detail requests.
func NewAgentStatsBundleHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		window, err := system.ParseTimeWindowString(c.DefaultQuery("window", "12h"))
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		agentID := c.Param("agentId")
		resp := dto.AgentStatsBundleResponse{
			MessageSummary: system.BuildAgentMessageSummary(agentID, window),
			MessageTrend:   system.BuildAgentMessageDirectionalTimeSeries(agentID, window),
			MessageBar:     system.BuildAgentMessageBarSeries(agentID, window),
			TokenSummary:   system.BuildAgentTokenSummary(agentID, window),
			TokenTrend:     system.BuildAgentTokenTimeSeries(agentID, window),
			InputTokenBar:  system.BuildAgentModelTokenBarSeries(agentID, window, "input"),
			CachedTokenBar: system.BuildAgentModelTokenBarSeries(agentID, window, "cached"),
			OutputTokenBar: system.BuildAgentModelTokenBarSeries(agentID, window, "output"),
		}
		c.JSON(http.StatusOK, resp)
	}
}
