package task

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

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

func (handler *CollaboratorHandler) collaboratorBoard() (TaskCollaboratorBoard, error) {
	if handler.collaborators == nil {
		return nil, api.NewHTTPError(http.StatusNotImplemented, "Task collaborators are unavailable")
	}
	return handler.collaborators, nil
}

// ListTask handles GET /api/tasks/:taskId/collaborators.
// It lists explicit collaborators for a task.
func (handler *CollaboratorHandler) ListTask(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.list(r, taskboard.TaskRef{TaskID: strings.TrimSpace(r.Gin.Param("taskId"))}, taskboard.TaskCollaboratorScopeRef{})
}

// AddTask handles POST /api/tasks/:taskId/collaborators.
// It adds or updates an explicit collaborator for a task.
func (handler *CollaboratorHandler) AddTask(r api.WorkspaceScopedJSON[dto.TaskCollaboratorRequest]) (api.Response, error) {
	return handler.add(r, taskboard.TaskRef{TaskID: strings.TrimSpace(r.Gin.Param("taskId"))}, taskboard.TaskCollaboratorScopeRef{})
}

// RemoveTask handles DELETE /api/tasks/:taskId/collaborators/:memberId.
// It removes an explicit collaborator from a task.
func (handler *CollaboratorHandler) RemoveTask(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.remove(r, taskboard.TaskRef{TaskID: strings.TrimSpace(r.Gin.Param("taskId"))}, taskboard.TaskCollaboratorScopeRef{}, strings.TrimSpace(r.Gin.Param("memberId")))
}

// LeaveTask handles POST /api/tasks/:taskId/collaborators/leave.
// It removes the current user from the task collaborator list.
func (handler *CollaboratorHandler) LeaveTask(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.leave(r, taskboard.TaskRef{TaskID: strings.TrimSpace(r.Gin.Param("taskId"))}, taskboard.TaskCollaboratorScopeRef{})
}

// ListEffectiveTask handles GET /api/tasks/:taskId/effective-collaborators.
// It lists explicit and inherited collaborators that can access a task.
func (handler *CollaboratorHandler) ListEffectiveTask(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.listEffective(r, taskboard.TaskRef{TaskID: strings.TrimSpace(r.Gin.Param("taskId"))}, taskboard.TaskCollaboratorScopeRef{})
}

// ListEffectiveThread handles GET /api/threads/:threadChannelId/task-collaborators/effective.
// It lists explicit and channel-inherited collaborators for a reply thread execution surface.
func (handler *CollaboratorHandler) ListEffectiveThread(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.listEffective(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "thread", "threadChannelId"))
}

func (handler *CollaboratorHandler) listEffective(r api.WorkspaceScopedRequest, taskRef taskboard.TaskRef, scope taskboard.TaskCollaboratorScopeRef) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	if strings.TrimSpace(taskRef.TaskID) == "" && strings.TrimSpace(scope.ScopeID) == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "collaborator scope is required")
	}
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.ListEffectiveTaskCollaborators(r.Context(), taskboard.ListEffectiveTaskCollaboratorsRequest{
		Context: taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		Task:    taskRef,
		Scope:   scope,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToListEffectiveTaskCollaboratorsResponse(response)), nil
}

// ResolveTaskPrivateAgentContextState handles POST /api/tasks/:taskId/private-agent-context-state.
// It resolves private-agent context state for task-scoped messages.
func (handler *CollaboratorHandler) ResolveTaskPrivateAgentContextState(r api.WorkspaceScopedJSON[dto.PrivateAgentContextStateRequest]) (api.Response, error) {
	return handler.resolvePrivateAgentContextState(r, taskboard.TaskRef{TaskID: strings.TrimSpace(r.Gin.Param("taskId"))}, taskboard.TaskCollaboratorScopeRef{})
}

// ResolveTaskPrivateAgentContextContributionCapability handles GET /api/tasks/:taskId/private-agent-contribution-capability.
// It resolves whether the current author can contribute private-agent context for the task.
func (handler *CollaboratorHandler) ResolveTaskPrivateAgentContextContributionCapability(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.resolvePrivateAgentContextContributionCapability(r, taskboard.TaskRef{TaskID: strings.TrimSpace(r.Gin.Param("taskId"))}, taskboard.TaskCollaboratorScopeRef{})
}

// ListThread handles GET /api/threads/:threadChannelId/task-collaborators.
// It lists explicit collaborators for a reply thread execution surface.
func (handler *CollaboratorHandler) ListThread(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.list(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "thread", "threadChannelId"))
}

// AddThread handles POST /api/threads/:threadChannelId/task-collaborators.
// It adds or updates an explicit collaborator for a reply thread execution surface.
func (handler *CollaboratorHandler) AddThread(r api.WorkspaceScopedJSON[dto.TaskCollaboratorRequest]) (api.Response, error) {
	return handler.add(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r.WorkspaceScopedRequest, "thread", "threadChannelId"))
}

// RemoveThread handles DELETE /api/threads/:threadChannelId/task-collaborators/:memberId.
// It removes an explicit collaborator from a reply thread execution surface.
func (handler *CollaboratorHandler) RemoveThread(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.remove(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "thread", "threadChannelId"), strings.TrimSpace(r.Gin.Param("memberId")))
}

// LeaveThread handles POST /api/threads/:threadChannelId/task-collaborators/leave.
// It removes the current user from a reply thread collaborator list.
func (handler *CollaboratorHandler) LeaveThread(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.leave(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "thread", "threadChannelId"))
}

// ResolveThreadPrivateAgentContextState handles POST /api/threads/:threadChannelId/private-agent-context-state.
// It resolves private-agent context state for thread-scoped messages.
func (handler *CollaboratorHandler) ResolveThreadPrivateAgentContextState(r api.WorkspaceScopedJSON[dto.PrivateAgentContextStateRequest]) (api.Response, error) {
	return handler.resolvePrivateAgentContextState(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r.WorkspaceScopedRequest, "thread", "threadChannelId"))
}

// ResolveThreadPrivateAgentContextContributionCapability handles GET /api/threads/:threadChannelId/private-agent-contribution-capability.
// It resolves whether the current author can contribute private-agent context for the thread.
func (handler *CollaboratorHandler) ResolveThreadPrivateAgentContextContributionCapability(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.resolvePrivateAgentContextContributionCapability(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "thread", "threadChannelId"))
}

// ListThreadPrivateAgentMentionCandidates handles GET /api/threads/:threadChannelId/private-agent-mention-candidates.
// It lazily lists private-agent mention candidates for a reply thread execution surface.
func (handler *CollaboratorHandler) ListThreadPrivateAgentMentionCandidates(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.listPrivateAgentMentionCandidates(r, taskCollaboratorScopeFromRequest(r, "thread", "threadChannelId"))
}

// ListChannel handles GET /api/channels/:channelId/task-collaborators.
// It lists explicit collaborators for a channel execution surface.
func (handler *CollaboratorHandler) ListChannel(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.list(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "channel", "channelId"))
}

// AddChannel handles POST /api/channels/:channelId/task-collaborators.
// It adds or updates an explicit collaborator for a channel execution surface.
func (handler *CollaboratorHandler) AddChannel(r api.WorkspaceScopedJSON[dto.TaskCollaboratorRequest]) (api.Response, error) {
	return handler.add(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r.WorkspaceScopedRequest, "channel", "channelId"))
}

// RemoveChannel handles DELETE /api/channels/:channelId/task-collaborators/:memberId.
// It removes an explicit collaborator from a channel execution surface.
func (handler *CollaboratorHandler) RemoveChannel(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.remove(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "channel", "channelId"), strings.TrimSpace(r.Gin.Param("memberId")))
}

// LeaveChannel handles POST /api/channels/:channelId/task-collaborators/leave.
// It removes the current user from a channel collaborator list.
func (handler *CollaboratorHandler) LeaveChannel(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.leave(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "channel", "channelId"))
}

// ResolveChannelPrivateAgentContextState handles POST /api/channels/:channelId/private-agent-context-state.
// It resolves private-agent context state for channel-scoped messages.
func (handler *CollaboratorHandler) ResolveChannelPrivateAgentContextState(r api.WorkspaceScopedJSON[dto.PrivateAgentContextStateRequest]) (api.Response, error) {
	return handler.resolvePrivateAgentContextState(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r.WorkspaceScopedRequest, "channel", "channelId"))
}

// ResolveChannelPrivateAgentContextContributionCapability handles GET /api/channels/:channelId/private-agent-contribution-capability.
// It resolves whether the current author can contribute private-agent context for the channel.
func (handler *CollaboratorHandler) ResolveChannelPrivateAgentContextContributionCapability(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.resolvePrivateAgentContextContributionCapability(r, taskboard.TaskRef{}, taskCollaboratorScopeFromRequest(r, "channel", "channelId"))
}

// ListChannelPrivateAgentMentionCandidates handles GET /api/channels/:channelId/private-agent-mention-candidates.
// It lazily lists private-agent mention candidates for a channel execution surface.
func (handler *CollaboratorHandler) ListChannelPrivateAgentMentionCandidates(r api.WorkspaceScopedRequest) (api.Response, error) {
	return handler.listPrivateAgentMentionCandidates(r, taskCollaboratorScopeFromRequest(r, "channel", "channelId"))
}

func (handler *CollaboratorHandler) ListAgentCollaborators(r api.WorkspaceScopedRequest) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.ListAgentCollaborators(r.Context(), taskboard.ListAgentCollaboratorsRequest{
		Context:         taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		SourceScopeType: strings.TrimSpace(r.Gin.Query("sourceScopeType")),
		SourceScopeID:   strings.TrimSpace(r.Gin.Query("sourceScopeId")),
		IncludeInactive: strings.EqualFold(strings.TrimSpace(r.Gin.Query("includeInactive")), "true"),
		Limit:           parsePositiveIntQuery(r, "limit"),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToListAgentCollaboratorsResponse(response)), nil
}

func taskCollaboratorScopeFromRequest(r api.WorkspaceScopedRequest, scopeType string, paramName string) taskboard.TaskCollaboratorScopeRef {
	return taskboard.TaskCollaboratorScopeRef{
		ScopeType:              scopeType,
		ScopeID:                strings.TrimSpace(r.Gin.Param(paramName)),
		ExecutionOwnerMemberID: strings.TrimSpace(r.Gin.Query("executionOwnerMemberId")),
	}
}

func parsePositiveIntQuery(r api.WorkspaceScopedRequest, key string) int {
	parsed, err := strconv.Atoi(strings.TrimSpace(r.Gin.Query(key)))
	if err != nil || parsed <= 0 {
		return 0
	}
	return parsed
}

func (handler *CollaboratorHandler) list(r api.WorkspaceScopedRequest, taskRef taskboard.TaskRef, scope taskboard.TaskCollaboratorScopeRef) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	if strings.TrimSpace(taskRef.TaskID) == "" && strings.TrimSpace(scope.ScopeID) == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "collaborator scope is required")
	}
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.ListTaskCollaborators(r.Context(), taskboard.ListTaskCollaboratorsRequest{
		Context:         taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		Task:            taskRef,
		Scope:           scope,
		IncludeInactive: strings.EqualFold(strings.TrimSpace(r.Gin.Query("includeInactive")), "true"),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToListTaskCollaboratorsResponse(response)), nil
}

func (handler *CollaboratorHandler) resolvePrivateAgentContextState(r api.WorkspaceScopedJSON[dto.PrivateAgentContextStateRequest], taskRef taskboard.TaskRef, scope taskboard.TaskCollaboratorScopeRef) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	body := r.Body
	if strings.TrimSpace(taskRef.TaskID) == "" && strings.TrimSpace(scope.ScopeID) == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "collaborator scope is required")
	}
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.ResolvePrivateAgentContextState(r.Context(), taskboard.ResolvePrivateAgentContextStateRequest{
		Context:    taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		Task:       taskRef,
		Scope:      scope,
		MessageIDs: body.MessageIDs,
		Window:     converter.FromPrivateAgentContextStateWindowInput(body.Window),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToResolvePrivateAgentContextStateResponse(response)), nil
}

func (handler *CollaboratorHandler) resolvePrivateAgentContextContributionCapability(r api.WorkspaceScopedRequest, taskRef taskboard.TaskRef, scope taskboard.TaskCollaboratorScopeRef) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	if strings.TrimSpace(taskRef.TaskID) == "" && strings.TrimSpace(scope.ScopeID) == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "collaborator scope is required")
	}
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.ResolvePrivateAgentContextContributionCapability(r.Context(), taskboard.ResolvePrivateAgentContextContributionCapabilityRequest{
		Context: taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		Task:    taskRef,
		Scope:   scope,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToResolvePrivateAgentContextContributionCapabilityResponse(response)), nil
}

func (handler *CollaboratorHandler) listPrivateAgentMentionCandidates(r api.WorkspaceScopedRequest, scope taskboard.TaskCollaboratorScopeRef) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	if strings.TrimSpace(scope.ScopeID) == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "collaborator scope is required")
	}
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.ListPrivateAgentMentionCandidates(r.Context(), taskboard.ListPrivateAgentMentionCandidatesRequest{
		Context: taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		Scope:   scope,
		Prefix:  strings.TrimSpace(r.Gin.Query("prefix")),
		Limit:   parsePositiveIntQuery(r, "limit"),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToListPrivateAgentMentionCandidatesResponse(response)), nil
}

func (handler *CollaboratorHandler) add(r api.WorkspaceScopedJSON[dto.TaskCollaboratorRequest], taskRef taskboard.TaskRef, scope taskboard.TaskCollaboratorScopeRef) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	body := r.Body
	if strings.TrimSpace(taskRef.TaskID) == "" && strings.TrimSpace(scope.ScopeID) == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "collaborator scope is required")
	}
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.AddTaskCollaborator(r.Context(), taskboard.AddTaskCollaboratorRequest{
		Context: taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		Task:    taskRef,
		Scope:   scope,
		Collaborator: taskboard.TaskCollaboratorInput{
			MemberID:  body.MemberID,
			Role:      body.Role,
			ExpiresAt: body.ExpiresAt,
		},
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToAddTaskCollaboratorResponse(response)), nil
}

func (handler *CollaboratorHandler) remove(r api.WorkspaceScopedRequest, taskRef taskboard.TaskRef, scope taskboard.TaskCollaboratorScopeRef, memberID string) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	if strings.TrimSpace(taskRef.TaskID) == "" && strings.TrimSpace(scope.ScopeID) == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "collaborator scope is required")
	}
	if memberID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "memberId is required")
	}
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.RemoveTaskCollaborator(r.Context(), taskboard.RemoveTaskCollaboratorRequest{
		Context:  taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		Task:     taskRef,
		Scope:    scope,
		MemberID: memberID,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToRemoveTaskCollaboratorResponse(response)), nil
}

func (handler *CollaboratorHandler) leave(r api.WorkspaceScopedRequest, taskRef taskboard.TaskRef, scope taskboard.TaskCollaboratorScopeRef) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	if strings.TrimSpace(taskRef.TaskID) == "" && strings.TrimSpace(scope.ScopeID) == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "collaborator scope is required")
	}
	board, err := handler.collaboratorBoard()
	if err != nil {
		return api.Response{}, err
	}
	response, err := board.LeaveTaskCollaborator(r.Context(), taskboard.LeaveTaskCollaboratorRequest{
		Context: taskboard.NewUserServiceContext(workspaceID, authenticated.User.ID, authenticated.User.Name),
		Task:    taskRef,
		Scope:   scope,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToLeaveTaskCollaboratorResponse(response)), nil
}
