package webagent

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

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/api/dto/converter"
)

// Directory handles GET /api/agents/directory.
// It returns one bounded page of lightweight agent directory rows without
// runtime, diagnostics, or LLM configuration hydration.
func (handler *CollectionHandler) Directory(r api.WorkspaceScopedRequest) (api.Response, error) {
	if handler.agentStore == nil {
		return api.Response{}, api.ErrNotFound
	}
	options, err := agentDirectoryOptionsFromRequest(r)
	if err != nil {
		return api.Response{}, err
	}
	page, err := handler.agentStore.ListAgentDirectoryForWorkspace(r.Context(), r.UserID(), r.WorkspaceID(), options)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.AgentDirectoryResponse{
		Agents:     converter.ToAgentDirectoryItems(page.Agents, options.IncludeReview),
		Limit:      page.Limit,
		Cursor:     page.Cursor,
		HasMore:    page.HasMore,
		NextCursor: page.NextCursor,
	}), nil
}

// Search handles GET /api/agents/search.
// It returns a bounded prefix-search page of lightweight agent directory rows.
func (handler *CollectionHandler) Search(r api.WorkspaceScopedRequest) (api.Response, error) {
	if handler.agentStore == nil {
		return api.Response{}, api.ErrNotFound
	}
	options, err := agentDirectoryOptionsFromRequest(r)
	if err != nil {
		return api.Response{}, err
	}
	page, err := handler.agentStore.ListAgentDirectoryForWorkspace(r.Context(), r.UserID(), r.WorkspaceID(), options)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.AgentDirectoryResponse{
		Agents:     converter.ToAgentDirectoryItems(page.Agents, options.IncludeReview),
		Limit:      page.Limit,
		Cursor:     page.Cursor,
		HasMore:    page.HasMore,
		NextCursor: page.NextCursor,
	}), nil
}

// Lookup handles POST /api/v1/workspaces/:workspaceId/agents/lookup.
// It resolves at most 50 exact IDs through the active Workspace Agent
// directory without enumerating unrelated Agents.
func (handler *CollectionHandler) Lookup(r api.WorkspaceScopedJSON[dto.AgentDirectoryLookupRequest]) (api.Response, error) {
	if handler.agentStore == nil {
		return api.Response{}, api.ErrNotFound
	}
	if len(r.Body.AgentIDs) > maxAgentDirectoryLookupIDs {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "too many agentIds")
	}
	agentIDs, valid := normalizeLookupIDs(r.Body.AgentIDs)
	if !valid {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "invalid agentId")
	}
	if len(agentIDs) == 0 {
		return api.OK(dto.AgentDirectoryResponse{Agents: []dto.AgentDirectoryItem{}}), nil
	}
	items, err := handler.agentStore.LookupAgentDirectoryForWorkspace(
		r.Context(),
		r.UserID(),
		r.WorkspaceID(),
		agentIDs,
	)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.AgentDirectoryResponse{
		Agents: converter.ToAgentDirectoryItems(items, false),
		Limit:  len(agentIDs),
	}), nil
}

// Detail handles GET /api/agents/:agentId.
// It returns one agent detail read model for the requested agent ID without
// loading runtime diagnostics by default.
func (handler *CollectionHandler) Detail(r api.WorkspaceScopedRequest) (api.Response, error) {
	if handler.agentStore == nil {
		return api.Response{}, api.ErrNotFound
	}
	agentID := strings.TrimSpace(r.Gin.Param("agentId"))
	if agentID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "agentId is required")
	}
	detail, err := handler.agentStore.AgentDetailForWorkspace(r.Context(), r.UserID(), r.WorkspaceID(), agentID)
	if err != nil {
		return api.Response{}, err
	}
	api.OverlayAppAgentRuntime(&detail, handler.runtime, time.Now().UTC())
	return api.OK(converter.ToAppAgent(detail)), nil
}

// RuntimeFailure handles GET /api/agents/:agentId/runtime-failure.
// It lazily returns the latest runtime failure for one authorized diagnostic
// target, or null when the agent has no current failure summary.
func (handler *CollectionHandler) RuntimeFailure(r api.WorkspaceScopedRequest) (api.Response, error) {
	if handler.agentStore == nil {
		return api.Response{}, api.ErrNotFound
	}
	agentID := strings.TrimSpace(r.Gin.Param("agentId"))
	if agentID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "agentId is required")
	}
	failure, err := handler.agentStore.AgentRuntimeFailureForWorkspace(r.Context(), r.UserID(), r.WorkspaceID(), agentID)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.AgentRuntimeFailureItem{
		AgentID: agentID,
		Failure: converter.ToAgentRuntimeFailure(failure),
	}), nil
}

// RuntimeFailures handles POST /api/agents/runtime-failures.
// It returns latest runtime failures only for the bounded requested agent IDs
// that the caller may inspect.
func (handler *CollectionHandler) RuntimeFailures(r api.WorkspaceScopedJSON[dto.AgentRuntimeFailuresRequest]) (api.Response, error) {
	if handler.agentStore == nil {
		return api.Response{}, api.ErrNotFound
	}
	agentIDs := normalizeRequestedAgentIDs(r.Body.AgentIDs)
	if len(agentIDs) == 0 {
		return api.OK(dto.AgentRuntimeFailuresResponse{Failures: []dto.AgentRuntimeFailureItem{}}), nil
	}
	if len(agentIDs) > maxAgentRuntimeFailureIDs {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "too many agentIds")
	}
	failures, err := handler.agentStore.AgentRuntimeFailuresForWorkspace(r.Context(), r.UserID(), r.WorkspaceID(), agentIDs)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.AgentRuntimeFailuresResponse{Failures: converter.ToAgentRuntimeFailureItems(failures)}), nil
}
