package agent

import (
	"context"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"

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

// TriggerHandler handles CRUD and lifecycle operations for agent-owned triggers.
type TriggerHandler struct {
	auth     AgentAuthenticator
	owners   TriggerOwnerStore
	triggers TriggerControlService
}

// TriggerControlService provides the trigger lifecycle API scoped to an owner.
type TriggerControlService interface {
	ListSummariesForOwnerWithFilter(ctx context.Context, ownerUserID string, workspaceID string, filter trigger.ListFilter) ([]trigger.TriggerSummaryView, error)
	ValidateForOwner(ctx context.Context, ownerUserID string, workspaceID string, input trigger.UpsertInput, existingTriggerID string) (trigger.ValidateResult, error)
	CreateForOwner(ctx context.Context, ownerUserID string, workspaceID string, input trigger.UpsertInput) (trigger.TriggerView, error)
	GetForOwner(ctx context.Context, ownerUserID string, workspaceID string, triggerID string) (trigger.TriggerView, error)
	UpdateForOwner(ctx context.Context, ownerUserID string, workspaceID string, triggerID string, input trigger.UpsertInput) (trigger.TriggerView, error)
	PauseForOwner(ctx context.Context, ownerUserID string, workspaceID string, triggerID string) (trigger.TriggerView, error)
	ResumeForOwner(ctx context.Context, ownerUserID string, workspaceID string, triggerID string) (trigger.TriggerView, error)
	DeleteForOwner(ctx context.Context, ownerUserID string, workspaceID string, triggerID string) error
	ListRunsForOwner(ctx context.Context, ownerUserID string, workspaceID string, triggerID string, limit int) ([]trigger.RunView, error)
}

func parseBoolishQuery(raw string) bool {
	switch strings.TrimSpace(strings.ToLower(raw)) {
	case "1", "true", "yes":
		return true
	default:
		return false
	}
}

func parseOffsetCursor(raw string) (int, error) {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return 0, nil
	}
	offset, err := strconv.Atoi(trimmed)
	if err != nil || offset < 0 {
		return 0, fmt.Errorf("invalid cursor")
	}
	return offset, nil
}

func (h *TriggerHandler) requireTriggerService(c *gin.Context) TriggerControlService {
	if h.triggers == nil {
		c.JSON(http.StatusNotImplemented, agentError("Trigger service is not configured"))
		return nil
	}
	return h.triggers
}

// List returns all triggers owned by the agent's managing user.
func (h *TriggerHandler) List(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	triggers := h.requireTriggerService(c)
	if triggers == nil {
		return
	}

	ownerUserID, err := h.owners.AgentManagedByUser(c.Request.Context(), agent.WorkspaceID, agent.AgentID)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}

	limit, err := ParseLimit(c.Query("limit"), trigger.DefaultListLimit)
	if err != nil {
		c.JSON(http.StatusBadRequest, agentError(err.Error()))
		return
	}
	if limit > trigger.MaxListLimit {
		limit = trigger.MaxListLimit
	}
	cursor := strings.TrimSpace(c.Query("cursor"))
	pageMode := cursor != "" || parseBoolishQuery(c.Query("page"))
	offset, err := parseOffsetCursor(cursor)
	if err != nil {
		c.JSON(http.StatusBadRequest, agentError(err.Error()))
		return
	}
	queryLimit := limit
	if pageMode {
		queryLimit = limit + 1
	}

	result, err := triggers.ListSummariesForOwnerWithFilter(c.Request.Context(), ownerUserID, agent.WorkspaceID, trigger.ListFilter{
		Limit:  queryLimit,
		Offset: offset,
		Page:   pageMode,
	})
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}
	if pageMode {
		hasMore := len(result) > limit
		if hasMore {
			result = result[:limit]
		}
		nextCursor := ""
		if hasMore {
			nextCursor = strconv.Itoa(offset + limit)
		}
		c.JSON(http.StatusOK, gin.H{
			"triggers":    result,
			"limit":       limit,
			"cursor":      cursor,
			"has_more":    hasMore,
			"next_cursor": nextCursor,
		})
		return
	}
	if len(result) > limit {
		result = result[:limit]
	}
	c.JSON(http.StatusOK, result)
}

// Validate checks a trigger configuration without persisting it.
func (h *TriggerHandler) Validate(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	triggers := h.requireTriggerService(c)
	if triggers == nil {
		return
	}

	var request dto.AgentTriggerValidateRequest
	if !bindJSON(c, &request) {
		return
	}

	ownerUserID, input, err := h.triggerInputForAgent(c.Request.Context(), agent, request.AgentTriggerMutationRequest)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}

	result, err := triggers.ValidateForOwner(c.Request.Context(), ownerUserID, agent.WorkspaceID, input, strings.TrimSpace(request.TriggerID))
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}
	c.JSON(http.StatusOK, result)
}

// Create persists a new trigger for the agent's managing user.
func (h *TriggerHandler) Create(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	triggers := h.requireTriggerService(c)
	if triggers == nil {
		return
	}

	var request dto.AgentTriggerMutationRequest
	if !bindJSON(c, &request) {
		return
	}

	ownerUserID, input, err := h.triggerInputForAgent(c.Request.Context(), agent, request)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}

	result, err := triggers.CreateForOwner(c.Request.Context(), ownerUserID, agent.WorkspaceID, input)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}
	c.JSON(http.StatusOK, result)
}

// Get returns a single trigger by ID.
func (h *TriggerHandler) Get(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	triggers := h.requireTriggerService(c)
	if triggers == nil {
		return
	}

	ownerUserID, err := h.owners.AgentManagedByUser(c.Request.Context(), agent.WorkspaceID, agent.AgentID)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}

	result, err := triggers.GetForOwner(c.Request.Context(), ownerUserID, agent.WorkspaceID, scheduleOrTriggerID(c))
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}
	c.JSON(http.StatusOK, result)
}

// Update modifies an existing trigger's configuration.
func (h *TriggerHandler) Update(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	triggers := h.requireTriggerService(c)
	if triggers == nil {
		return
	}

	var request dto.AgentTriggerMutationRequest
	if !bindJSON(c, &request) {
		return
	}

	ownerUserID, input, err := h.triggerInputForAgent(c.Request.Context(), agent, request)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}

	result, err := triggers.UpdateForOwner(
		c.Request.Context(),
		ownerUserID,
		agent.WorkspaceID,
		scheduleOrTriggerID(c),
		input,
	)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}
	c.JSON(http.StatusOK, result)
}

// Pause suspends a trigger without deleting it.
func (h *TriggerHandler) Pause(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	h.handleLifecycleMutation(c, agent, func(ctx context.Context, triggerService TriggerControlService, ownerUserID string, workspaceID string, triggerID string) (trigger.TriggerView, error) {
		return triggerService.PauseForOwner(ctx, ownerUserID, workspaceID, triggerID)
	})
}

// Resume reactivates a paused trigger.
func (h *TriggerHandler) Resume(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	h.handleLifecycleMutation(c, agent, func(ctx context.Context, triggerService TriggerControlService, ownerUserID string, workspaceID string, triggerID string) (trigger.TriggerView, error) {
		return triggerService.ResumeForOwner(ctx, ownerUserID, workspaceID, triggerID)
	})
}

// Delete permanently removes a trigger.
func (h *TriggerHandler) Delete(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	triggers := h.requireTriggerService(c)
	if triggers == nil {
		return
	}

	ownerUserID, err := h.owners.AgentManagedByUser(c.Request.Context(), agent.WorkspaceID, agent.AgentID)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}

	if err := triggers.DeleteForOwner(c.Request.Context(), ownerUserID, agent.WorkspaceID, scheduleOrTriggerID(c)); err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}
	c.JSON(http.StatusOK, dto.AgentTriggerDeleteResponse{Deleted: true})
}

// ListRuns returns recent execution history for a trigger.
func (h *TriggerHandler) ListRuns(c *gin.Context) {
	agent, ok := authenticate(c, h.auth)
	if !ok {
		return
	}
	triggers := h.requireTriggerService(c)
	if triggers == nil {
		return
	}

	ownerUserID, err := h.owners.AgentManagedByUser(c.Request.Context(), agent.WorkspaceID, agent.AgentID)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}

	limit, err := ParseLimit(c.Query("limit"), trigger.DefaultListLimit)
	if err != nil {
		c.JSON(http.StatusBadRequest, agentError(err.Error()))
		return
	}
	if limit > trigger.MaxListLimit {
		limit = trigger.MaxListLimit
	}

	result, err := triggers.ListRunsForOwner(c.Request.Context(), ownerUserID, agent.WorkspaceID, scheduleOrTriggerID(c), limit)
	if err != nil {
		c.JSON(HTTPStatus(err), agentError(err.Error()))
		return
	}
	c.JSON(http.StatusOK, result)
}
