// Package task contains browser-facing task board and task routes.
package task

import (
	"log/slog"

	"github.com/gin-gonic/gin"

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

func handleTaskWorkspace(handler api.WorkspaceScopedHandler) gin.HandlerFunc {
	return api.HandleWorkspaceScoped(handler)
}

func handleTaskWorkspaceJSON[T any](
	handler func(api.WorkspaceScopedJSON[T]) (api.Response, error),
	opts ...api.HandlerOption,
) gin.HandlerFunc {
	return api.HandleWorkspaceScopedJSON[T](handler, opts...)
}

// Dependencies contains collaborators and config for task routes.
type Dependencies struct {
	AuthStore                   api.SessionAuthenticator
	WorkspaceStore              api.WorkspaceResolver
	EmailVerificationRequired   bool
	TaskBoard                   *taskboard.Store
	ReviewStore                 TaskReviewContextStore
	Store                       TaskParityStore
	MessageFanout               MessageFanout
	UnreadRuntime               TaskUnreadRuntime
	ConversationReadProjector   TaskConversationReadProjector
	ConversationPolicyProjector TaskConversationPolicyProjector
	CollaboratorRequests        CollaboratorRequestService
	Logger                      *slog.Logger
}

// RegisterRoutes mounts browser-facing task board and task routes.
func RegisterRoutes(router gin.IRouter, dependencies Dependencies) {
	if router == nil {
		return
	}
	apiGroup := router.Group("/api")
	workspaceScoped := apiGroup.Group(
		"/v1/workspaces/:workspaceId",
		api.RequireAuthenticatedUser(dependencies.AuthStore),
		api.RequireVerifiedIdentity(dependencies.EmailVerificationRequired),
		api.RequireRouteWorkspace(dependencies.WorkspaceStore),
	)
	handlers := newRouteHandlers(dependencies)
	reviewContexts := &TaskReviewContextHandler{store: dependencies.ReviewStore}
	registerBoardOptionRoutes(workspaceScoped, handlers.boardOptions)
	registerTaskBoardViewStateRoutes(workspaceScoped, handlers.boardView)
	registerTaskBrowseRoutes(workspaceScoped, handlers.browse)
	registerTaskCollectionRoutes(workspaceScoped, handlers.collection)
	registerTaskDetailRoutes(workspaceScoped, handlers.detail)
	workspaceScoped.GET("/tasks/:taskId/review-contexts", handleTaskWorkspace(reviewContexts.List))
	registerTaskLifecycleRoutes(workspaceScoped, handlers.lifecycle)
	registerSubtaskRoutes(workspaceScoped, handlers.subtasks)
	registerTaskCheckpointRoutes(workspaceScoped, handlers.checkpoints)
	registerTaskCollaboratorRoutes(workspaceScoped, handlers.collaborators)
	registerTaskCollaboratorRequestRoutes(workspaceScoped, handlers.collaboratorRequests)
	registerTaskParityRoutes(workspaceScoped, handlers.parity)
}

func registerBoardOptionRoutes(workspaceScoped gin.IRoutes, handler *BoardOptionHandler) {
	workspaceScoped.GET("/task-board/options", handleTaskWorkspace(handler.List))
	workspaceScoped.POST("/task-board/options", handleTaskWorkspaceJSON[dto.TaskBoardOptionRequest](handler.Create))
	workspaceScoped.PATCH("/task-board/options/:optionId", handleTaskWorkspaceJSON[dto.TaskBoardOptionRequest](handler.Update))
	workspaceScoped.POST("/task-board/options/:optionId/reorder", handleTaskWorkspaceJSON[dto.TaskBoardOptionReorderRequest](handler.Reorder))
	workspaceScoped.POST("/task-board/options/:optionId/deactivate", handleTaskWorkspace(handler.Deactivate))
	workspaceScoped.POST("/task-board/options/:optionId/reactivate", handleTaskWorkspace(handler.Reactivate))
}

func registerTaskBoardViewStateRoutes(workspaceScoped gin.IRoutes, handler *BoardViewStateHandler) {
	workspaceScoped.GET("/task-board/saved-views", handleTaskWorkspace(handler.ListSavedViews))
	workspaceScoped.POST("/task-board/saved-views", handleTaskWorkspaceJSON[dto.TaskBoardSavedViewRequest](handler.CreateSavedView))
	workspaceScoped.PATCH("/task-board/saved-views/:savedViewId", handleTaskWorkspaceJSON[dto.TaskBoardSavedViewRequest](handler.UpdateSavedView))
	workspaceScoped.DELETE("/task-board/saved-views/:savedViewId", handleTaskWorkspace(handler.DeleteSavedView))
	workspaceScoped.GET("/tasks/channel/:channelId/view-state", handleTaskWorkspace(handler.Get))
	workspaceScoped.PUT("/tasks/channel/:channelId/view-state", handleTaskWorkspaceJSON[dto.TaskBoardViewStateRequest](handler.Set))
}

func registerTaskBrowseRoutes(workspaceScoped gin.IRoutes, handler *BrowseHandler) {
	workspaceScoped.GET("/tasks/filter-options", handleTaskWorkspace(handler.FilterOptions))
	workspaceScoped.GET("/tasks/related/thread/:threadChannelId/summary", handleTaskWorkspace(handler.RelatedTasksSummaryForThread))
	workspaceScoped.GET("/tasks/related/thread/:threadChannelId", handleTaskWorkspace(handler.RelatedTasksForThread))
	workspaceScoped.GET("/tasks/related/agents/:agentId/execution/summary", handleTaskWorkspace(handler.RelatedExecutionTasksSummaryForAgent))
	workspaceScoped.GET("/tasks/related/agents/:agentId/execution", handleTaskWorkspace(handler.RelatedExecutionTasksForAgent))
	workspaceScoped.POST("/tasks/workspace/browse", handleTaskWorkspaceJSON[dto.TaskBrowseRequest](handler.WorkspaceTasks))
	workspaceScoped.POST("/tasks/workspace/board/browse", handleTaskWorkspaceJSON[dto.TaskBrowseRequest](handler.WorkspaceBoard))
	workspaceScoped.POST("/tasks/channel/:channelId/browse", handleTaskWorkspaceJSON[dto.TaskBrowseRequest](handler.ChannelTasks))
	workspaceScoped.POST("/tasks/channel/:channelId/board/browse", handleTaskWorkspaceJSON[dto.TaskBrowseRequest](handler.ChannelBoard))
}

func registerTaskCollectionRoutes(workspaceScoped gin.IRoutes, handler *CollectionHandler) {
	// Canonical resource-oriented Task collection routes. Channel owns the
	// board collection; the authenticated actor comes from request context.
	workspaceScoped.GET("/channels/:channelId/tasks", handleTaskWorkspace(handler.ResourceList))
	workspaceScoped.POST("/channels/:channelId/tasks", handleTaskWorkspaceJSON[dto.TaskCreateRequest](handler.Create))
}

func registerTaskDetailRoutes(workspaceScoped gin.IRoutes, handler *DetailHandler) {
	// Canonical Task item routes. Task identity is workspace-scoped; the
	// channel-number route is a human-visible locator for the same resource.
	workspaceScoped.GET("/channels/:channelId/tasks/:taskNumber", handleTaskWorkspace(handler.GetByNumber))
	workspaceScoped.GET("/tasks/:taskId", handleTaskWorkspace(handler.Get))
	workspaceScoped.GET("/tasks/:taskId/timeline", handleTaskWorkspace(handler.TimelineList))
	workspaceScoped.POST("/tasks/:taskId/read-state", handleTaskWorkspace(handler.MarkRead))
	workspaceScoped.PATCH("/tasks/:taskId", handleTaskWorkspaceJSON[dto.TaskUpdateRequest](handler.Patch))
	workspaceScoped.DELETE("/tasks/:taskId", handleTaskWorkspace(handler.Delete))
}

func registerTaskLifecycleRoutes(workspaceScoped gin.IRoutes, handler *LifecycleHandler) {
	workspaceScoped.PATCH("/tasks/:taskId/claim", handleTaskWorkspace(handler.Claim))
	workspaceScoped.PATCH("/tasks/:taskId/unclaim", handleTaskWorkspace(handler.Unclaim))
	workspaceScoped.PATCH("/tasks/:taskId/block", handleTaskWorkspaceJSON[dto.TaskBlockRequest](handler.Block))
	workspaceScoped.PATCH("/tasks/:taskId/unblock", handleTaskWorkspace(handler.Unblock))
}

func registerSubtaskRoutes(workspaceScoped gin.IRoutes, handler *SubtaskHandler) {
	workspaceScoped.POST("/tasks/:taskId/subtasks", handleTaskWorkspaceJSON[dto.TaskCreateSubtasksRequest](handler.Create))
	workspaceScoped.GET("/tasks/:taskId/subtasks", handleTaskWorkspace(handler.List))
}

func registerTaskCheckpointRoutes(workspaceScoped gin.IRoutes, handler *CheckpointHandler) {
	workspaceScoped.POST("/tasks/:taskId/checkpoints", handleTaskWorkspaceJSON[dto.TaskCheckpointRequest](handler.Create))
}

func registerTaskCollaboratorRoutes(workspaceScoped gin.IRoutes, handler *CollaboratorHandler) {
	workspaceScoped.GET("/agent-collaborators", handleTaskWorkspace(handler.ListAgentCollaborators))

	workspaceScoped.GET("/tasks/:taskId/collaborators", handleTaskWorkspace(handler.ListTask))
	workspaceScoped.POST("/tasks/:taskId/collaborators", handleTaskWorkspaceJSON[dto.TaskCollaboratorRequest](handler.AddTask))
	workspaceScoped.DELETE("/tasks/:taskId/collaborators/:memberId", handleTaskWorkspace(handler.RemoveTask))
	workspaceScoped.POST("/tasks/:taskId/collaborators/leave", handleTaskWorkspace(handler.LeaveTask))
	workspaceScoped.GET("/tasks/:taskId/effective-collaborators", handleTaskWorkspace(handler.ListEffectiveTask))
	workspaceScoped.POST("/tasks/:taskId/private-agent-context-state", handleTaskWorkspaceJSON[dto.PrivateAgentContextStateRequest](handler.ResolveTaskPrivateAgentContextState))
	workspaceScoped.GET("/tasks/:taskId/private-agent-contribution-capability", handleTaskWorkspace(handler.ResolveTaskPrivateAgentContextContributionCapability))
	workspaceScoped.GET("/tasks/:taskId/private-agent-sender-capability", handleTaskWorkspace(handler.ResolveTaskPrivateAgentContextContributionCapability))

	workspaceScoped.GET("/threads/:threadChannelId/task-collaborators", handleTaskWorkspace(handler.ListThread))
	workspaceScoped.GET("/threads/:threadChannelId/task-collaborators/effective", handleTaskWorkspace(handler.ListEffectiveThread))
	workspaceScoped.POST("/threads/:threadChannelId/task-collaborators", handleTaskWorkspaceJSON[dto.TaskCollaboratorRequest](handler.AddThread))
	workspaceScoped.DELETE("/threads/:threadChannelId/task-collaborators/:memberId", handleTaskWorkspace(handler.RemoveThread))
	workspaceScoped.POST("/threads/:threadChannelId/task-collaborators/leave", handleTaskWorkspace(handler.LeaveThread))
	workspaceScoped.POST("/threads/:threadChannelId/private-agent-context-state", handleTaskWorkspaceJSON[dto.PrivateAgentContextStateRequest](handler.ResolveThreadPrivateAgentContextState))
	workspaceScoped.GET("/threads/:threadChannelId/private-agent-contribution-capability", handleTaskWorkspace(handler.ResolveThreadPrivateAgentContextContributionCapability))
	workspaceScoped.GET("/threads/:threadChannelId/private-agent-sender-capability", handleTaskWorkspace(handler.ResolveThreadPrivateAgentContextContributionCapability))
	workspaceScoped.GET("/threads/:threadChannelId/private-agent-mention-candidates", handleTaskWorkspace(handler.ListThreadPrivateAgentMentionCandidates))

	workspaceScoped.GET("/channels/:channelId/task-collaborators", handleTaskWorkspace(handler.ListChannel))
	workspaceScoped.POST("/channels/:channelId/task-collaborators", handleTaskWorkspaceJSON[dto.TaskCollaboratorRequest](handler.AddChannel))
	workspaceScoped.DELETE("/channels/:channelId/task-collaborators/:memberId", handleTaskWorkspace(handler.RemoveChannel))
	workspaceScoped.POST("/channels/:channelId/task-collaborators/leave", handleTaskWorkspace(handler.LeaveChannel))
	workspaceScoped.POST("/channels/:channelId/private-agent-context-state", handleTaskWorkspaceJSON[dto.PrivateAgentContextStateRequest](handler.ResolveChannelPrivateAgentContextState))
	workspaceScoped.GET("/channels/:channelId/private-agent-contribution-capability", handleTaskWorkspace(handler.ResolveChannelPrivateAgentContextContributionCapability))
	workspaceScoped.GET("/channels/:channelId/private-agent-sender-capability", handleTaskWorkspace(handler.ResolveChannelPrivateAgentContextContributionCapability))
	workspaceScoped.GET("/channels/:channelId/private-agent-mention-candidates", handleTaskWorkspace(handler.ListChannelPrivateAgentMentionCandidates))
}

func registerTaskParityRoutes(workspaceScoped gin.IRoutes, handler *ParityHandler) {
	workspaceScoped.GET("/tasks/:taskId/execution-history", handleTaskWorkspace(handler.ExecutionHistory))
	workspaceScoped.POST("/tasks/:taskId/announcements", handleTaskWorkspaceJSON[dto.TaskAnnouncementRequest](handler.CreateAnnouncement))
	workspaceScoped.GET("/tasks/:taskId/notification-policy", handleTaskWorkspace(handler.GetNotificationPolicy))
	workspaceScoped.GET("/tasks/:taskId/watchers", handleTaskWorkspace(handler.ListWatchers))
	workspaceScoped.PUT("/tasks/:taskId/notification-policy/self", handleTaskWorkspaceJSON[dto.TaskNotificationPolicyRequest](handler.SetNotificationSelf, api.WithInvalidJSONMessage("Invalid notification request")))
	workspaceScoped.PUT("/tasks/:taskId/notification-policy/agents/:agentId", handleTaskWorkspaceJSON[dto.TaskNotificationPolicyRequest](handler.SetNotificationAgent, api.WithInvalidJSONMessage("Invalid notification request")))
	workspaceScoped.PUT("/tasks/:taskId/notification-policy/agents/all", handleTaskWorkspaceJSON[dto.TaskNotificationPolicyRequest](handler.SetNotificationAgents, api.WithInvalidJSONMessage("Invalid notification request")))
	workspaceScoped.GET("/tasks/:taskId/active-targets", handleTaskWorkspace(handler.ListActiveTargets))
	workspaceScoped.POST("/tasks/:taskId/active-target", handleTaskWorkspaceJSON[dto.TaskActiveTargetRequest](handler.SetActiveTarget, api.WithInvalidJSONMessage("Invalid active target request")))
	workspaceScoped.DELETE("/tasks/:taskId/active-target", handleTaskWorkspace(handler.ClearActiveTarget))
}

// registerTaskCollaboratorRequestRoutes mounts the Human collaborator-request
// submit/status/cancel endpoints.
func registerTaskCollaboratorRequestRoutes(workspaceScoped gin.IRoutes, handler *CollaboratorRequestHandler) {
	workspaceScoped.POST("/collaborator-requests", handleTaskWorkspaceJSON[dto.CollaboratorRequestSubmitRequest](handler.Submit))
	workspaceScoped.GET("/collaborator-requests", handleTaskWorkspace(handler.List))
	workspaceScoped.POST("/collaborator-requests/:approvalId/cancel", handleTaskWorkspaceJSON[dto.CollaboratorRequestCancelRequest](handler.Cancel))
}
