package plan

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

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

const defaultAgentEditMessageLimit = 30

type agentEditHandlerService interface {
	GetWake(context.Context, string, string) (dto.WorkflowPlanAgentEditWakeResponse, error)
	ListMessages(context.Context, string, string, int64, int) (dto.WorkflowPlanAgentEditMessagesResponse, error)
	ListAgents(context.Context, string, string, string, string, string, int) (dto.WorkflowPlanAgentEditAgentsResponse, error)
	CreateRequest(context.Context, agentEditCreateInput) (dto.WorkflowPlanAgentEditRequestResponse, bool, error)
}

type agentEditCreateInput struct {
	WorkspaceID       string
	PlanID            string
	UserID            string
	MemberID          string
	MemberDisplayName string
	Request           dto.WorkflowPlanAgentEditRequest
}

type AgentEditHandler struct {
	service    agentEditHandlerService
	authorizer planMutationAuthorizer
}

// GetWake handles GET /api/v1/workspaces/:workspaceId/workflow/plans/:planId/agent-edit/wake.
// It returns only the latest durable execution state so the Builder can restore
// its edit lock without eagerly loading the Workflow message history.
func (handler *AgentEditHandler) GetWake(r api.WorkspaceScopedRequest) (api.Response, error) {
	id, err := planID(r)
	if err != nil {
		return api.Response{}, err
	}
	response, err := handler.service.GetWake(r.Context(), r.WorkspaceID(), id)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(response), nil
}

// ListMessages handles GET /api/v1/workspaces/:workspaceId/workflow/plans/:planId/agent-edit/messages.
// It reads one bounded Workflow product-scope history page without mutating
// membership or ordinary read/unread state.
func (handler *AgentEditHandler) ListMessages(r api.WorkspaceScopedRequest) (api.Response, error) {
	id, err := planID(r)
	if err != nil {
		return api.Response{}, err
	}
	before, err := optionalPositiveInt64(r.Gin.Query("beforeSeq"), "beforeSeq")
	if err != nil {
		return api.Response{}, err
	}
	limit, err := boundedAgentEditLimit(r.Gin.Query("limit"), defaultAgentEditMessageLimit)
	if err != nil {
		return api.Response{}, err
	}
	response, err := handler.service.ListMessages(r.Context(), r.WorkspaceID(), id, before, limit)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(response), nil
}

// ListAgents handles GET /api/v1/workspaces/:workspaceId/workflow/plans/:planId/agent-edit/agents.
// It reuses the ordinary owned-Agent directory and never returns Teams,
// non-Agent members, or Agents owned by another Human.
func (handler *AgentEditHandler) ListAgents(r api.WorkspaceScopedRequest) (api.Response, error) {
	id, err := planID(r)
	if err != nil {
		return api.Response{}, err
	}
	limit, err := boundedAgentEditLimit(r.Gin.Query("limit"), 50)
	if err != nil {
		return api.Response{}, err
	}
	response, err := handler.service.ListAgents(
		r.Context(), r.UserID(), r.WorkspaceID(), id,
		strings.TrimSpace(r.Gin.Query("q")), strings.TrimSpace(r.Gin.Query("cursor")), limit,
	)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(response), nil
}

// CreateRequest handles POST /api/v1/workspaces/:workspaceId/workflow/plans/:planId/agent-edit/requests.
// It validates the saved Draft anchor and appends one idempotent Human request
// with an exact selected-Agent wake.
func (handler *AgentEditHandler) CreateRequest(r api.WorkspaceScopedJSON[dto.WorkflowPlanAgentEditRequest]) (api.Response, error) {
	id, err := planID(r.WorkspaceScopedRequest)
	if err != nil {
		return api.Response{}, err
	}
	if err := handler.authorizer.requireWorkspaceAuthoringAllowed(
		r.Context(), r.WorkspaceID(), id, r.Workspace.MemberID,
	); err != nil {
		return api.Response{}, err
	}
	response, inserted, err := handler.service.CreateRequest(r.Context(), agentEditCreateInput{
		WorkspaceID: r.WorkspaceID(), PlanID: id, UserID: r.UserID(),
		MemberID: r.Workspace.MemberID, MemberDisplayName: r.Workspace.MemberDisplayName,
		Request: r.Body,
	})
	if err != nil {
		return api.Response{}, err
	}
	status := http.StatusCreated
	if !inserted {
		status = http.StatusOK
	}
	return api.Response{Status: status, Body: response}, nil
}

func boundedAgentEditLimit(raw string, fallback int) (int, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return fallback, nil
	}
	value, err := strconv.Atoi(raw)
	if err != nil || value < 1 || value > 50 {
		return 0, publicError(http.StatusBadRequest, "invalid_limit", "limit must be between 1 and 50")
	}
	return value, nil
}

func optionalPositiveInt64(raw, field string) (int64, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return 0, nil
	}
	value, err := strconv.ParseInt(raw, 10, 64)
	if err != nil || value <= 0 {
		return 0, publicError(http.StatusBadRequest, "invalid_"+field, field+" must be a positive integer")
	}
	return value, nil
}
