package taskboard

import (
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

// PrincipalType identifies the caller principal carried by a task service
// request envelope.
type PrincipalType string

const (
	PrincipalTypeUser    PrincipalType = "user"
	PrincipalTypeAgent   PrincipalType = "agent"
	PrincipalTypeService PrincipalType = "service"
)

// ServiceContext is the transport-neutral identity/capability envelope that
// core passes into task service operations.
type ServiceContext struct {
	RequestID      string
	WorkspaceID    string
	Subject        SubjectContext
	InstallationID string
	Scopes         []string
}

// SubjectContext identifies the acting principal for one request.
type SubjectContext struct {
	PrincipalID   string
	PrincipalType PrincipalType
	MemberID      string
	DisplayName   string
}

func NewUserServiceContext(workspaceID string, userID string, displayName string) ServiceContext {
	return ServiceContext{
		WorkspaceID: strings.TrimSpace(workspaceID),
		Subject: SubjectContext{
			PrincipalID:   strings.TrimSpace(userID),
			PrincipalType: PrincipalTypeUser,
			DisplayName:   strings.TrimSpace(displayName),
		},
	}
}

func NewAgentServiceContext(workspaceID string, agentID string, memberID string) ServiceContext {
	return ServiceContext{
		WorkspaceID: strings.TrimSpace(workspaceID),
		Subject: SubjectContext{
			PrincipalID:   strings.TrimSpace(agentID),
			PrincipalType: PrincipalTypeAgent,
			MemberID:      strings.TrimSpace(memberID),
		},
	}
}

func NewServiceContext(workspaceID string, principalID string) ServiceContext {
	return ServiceContext{
		WorkspaceID: strings.TrimSpace(workspaceID),
		Subject: SubjectContext{
			PrincipalID:   strings.TrimSpace(principalID),
			PrincipalType: PrincipalTypeService,
		},
	}
}

func (ctx ServiceContext) requireWorkspaceID() (string, error) {
	workspaceID := strings.TrimSpace(ctx.WorkspaceID)
	if workspaceID == "" {
		return "", badRequest("workspaceId is required")
	}
	return workspaceID, nil
}

func (ctx ServiceContext) principalType() PrincipalType {
	return PrincipalType(strings.TrimSpace(string(ctx.Subject.PrincipalType)))
}

func (ctx ServiceContext) requireUser() (string, string, error) {
	if ctx.principalType() != PrincipalTypeUser {
		return "", "", badRequest("user principal is required")
	}
	userID := strings.TrimSpace(ctx.Subject.PrincipalID)
	if userID == "" {
		return "", "", badRequest("context subject principalId is required")
	}
	return userID, strings.TrimSpace(ctx.Subject.DisplayName), nil
}

func (ctx ServiceContext) requireAgentActor() (AgentActor, error) {
	if ctx.principalType() != PrincipalTypeAgent {
		return AgentActor{}, badRequest("agent principal is required")
	}
	workspaceID, err := ctx.requireWorkspaceID()
	if err != nil {
		return AgentActor{}, err
	}
	memberID := strings.TrimSpace(ctx.Subject.MemberID)
	if memberID == "" {
		return AgentActor{}, badRequest("context subject memberId is required")
	}
	principalID := strings.TrimSpace(ctx.Subject.PrincipalID)
	if principalID == "" {
		return AgentActor{}, badRequest("context subject principalId is required")
	}
	return AgentActor{
		WorkspaceID: workspaceID,
		MemberID:    memberID,
	}, nil
}

// BoardRef identifies one task board.
type BoardRef struct {
	ChannelID string
	Channel   string
}

func (ref BoardRef) requireChannelID() (string, error) {
	channelID := strings.TrimSpace(ref.ChannelID)
	if channelID == "" {
		return "", badRequest("channelId is required")
	}
	return channelID, nil
}

func (ref BoardRef) requireChannelTarget() (string, error) {
	channel := strings.TrimSpace(ref.Channel)
	if channel == "" {
		return "", badRequest("channel is required")
	}
	return channel, nil
}

// TaskRef identifies one task by opaque id or board-local number.
type TaskRef struct {
	TaskID     string
	TaskNumber int64
}

func (ref TaskRef) requireTaskID() (string, error) {
	taskID := strings.TrimSpace(ref.TaskID)
	if taskID == "" {
		return "", badRequest("taskId is required")
	}
	return taskID, nil
}

func (ref TaskRef) requireTaskNumber() (int64, error) {
	if ref.TaskNumber <= 0 {
		return 0, badRequest("taskNumber is required")
	}
	return ref.TaskNumber, nil
}

func taskNumbersFromRefs(refs []TaskRef) ([]int64, error) {
	if len(refs) == 0 {
		return nil, badRequest("at least one task is required")
	}
	taskNumbers := make([]int64, 0, len(refs))
	for _, ref := range refs {
		taskNumber, err := ref.requireTaskNumber()
		if err != nil {
			return nil, err
		}
		taskNumbers = append(taskNumbers, taskNumber)
	}
	return taskNumbers, nil
}

// ListTasksRequest asks for one board's tasks in an actor-neutral shape.
type ListTasksRequest struct {
	Context ServiceContext
	Board   BoardRef
	Status  string
	// IncludeSubtasks is opt-in so existing task list consumers keep their
	// top-level-only behavior by default.
	IncludeSubtasks bool
}

type ListTasksResponse struct {
	ChannelID  string
	Tasks      []Task
	TaskViews  []TaskView
	Limit      int
	TotalCount int64
	HasMore    bool
	NextCursor TaskBrowseCursor
}

type ListWorkspaceTasksRequest struct {
	Context ServiceContext
	Status  string
	// IncludeSubtasks is opt-in so existing workspace task hub consumers keep
	// their top-level-only behavior by default.
	IncludeSubtasks bool
}

type ListWorkspaceTasksResponse struct {
	Tasks []Task
}

type TaskCollaboratorScopeRef struct {
	ScopeType              string
	ScopeID                string
	ExecutionOwnerMemberID string
}

type TaskCollaboratorInput struct {
	MemberID  string
	Role      string
	ExpiresAt *time.Time
}

type ListTaskCollaboratorsRequest struct {
	Context         ServiceContext
	Task            TaskRef
	Scope           TaskCollaboratorScopeRef
	IncludeInactive bool
}

type ListTaskCollaboratorsResponse struct {
	// ExecutionOwnerMemberID is the authoritative server-derived execution owner
	// of the scope (never the viewer). It resolves even when the collaborator
	// list is empty so the request-access entry point has its owner.
	ExecutionOwnerMemberID string
	OwnerProfile           *TaskPrincipalProfile
	// Owner is retained for old collaborator clients and must be derived from
	// OwnerProfile at API/Web/daemon boundaries.
	Owner         TaskActor
	Collaborators []TaskCollaborator
}

type ListAgentCollaboratorsRequest struct {
	Context         ServiceContext
	SourceScopeType string
	SourceScopeID   string
	IncludeInactive bool
	Limit           int
}

type ListAgentCollaboratorsResponse struct {
	ExecutionOwnerMemberID string
	OwnerProfile           *TaskPrincipalProfile
	// Owner is retained for old collaborator clients and must be derived from
	// OwnerProfile at API/Web/daemon boundaries.
	Owner                    TaskActor
	SourceScopeDisplayName   string
	SourceChannelDisplayName string
	People                   []AgentCollaboratorSummary
	Collaborators            []AgentCollaboratorSummary
}

type AddTaskCollaboratorRequest struct {
	Context      ServiceContext
	Task         TaskRef
	Scope        TaskCollaboratorScopeRef
	Collaborator TaskCollaboratorInput
}

type AddTaskCollaboratorResponse struct {
	Collaborator TaskCollaborator
}

type RemoveTaskCollaboratorRequest struct {
	Context  ServiceContext
	Task     TaskRef
	Scope    TaskCollaboratorScopeRef
	MemberID string
}

type RemoveTaskCollaboratorResponse struct {
	Collaborator TaskCollaborator
}

type LeaveTaskCollaboratorRequest struct {
	Context ServiceContext
	Task    TaskRef
	Scope   TaskCollaboratorScopeRef
}

type LeaveTaskCollaboratorResponse struct {
	Collaborator TaskCollaborator
}

type ListEffectiveTaskCollaboratorsRequest struct {
	Context ServiceContext
	Task    TaskRef
	Scope   TaskCollaboratorScopeRef
}

type ListEffectiveTaskCollaboratorsResponse struct {
	ExecutionOwnerMemberID string
	OwnerProfile           *TaskPrincipalProfile
	// Owner is retained for old collaborator clients and must be derived from
	// OwnerProfile at API/Web/daemon boundaries.
	Owner         TaskActor
	People        []EffectiveTaskCollaborator
	Collaborators []EffectiveTaskCollaborator
}

type PrivateAgentContextStateWindowInput struct {
	AfterSeq  *int64
	BeforeSeq *int64
	Limit     int
}

type ResolvePrivateAgentContextStateRequest struct {
	Context    ServiceContext
	Task       TaskRef
	Scope      TaskCollaboratorScopeRef
	MessageIDs []string
	Window     *PrivateAgentContextStateWindowInput
}

type ResolvePrivateAgentContextStateResponse struct {
	States []PrivateAgentContextState
}

type ResolvePrivateAgentContextContributionCapabilityRequest struct {
	Context ServiceContext
	Task    TaskRef
	Scope   TaskCollaboratorScopeRef
}

type ResolvePrivateAgentContextContributionCapabilityResponse struct {
	TargetScopeType        string
	TargetScopeID          string
	ExecutionOwnerMemberID string
	Relation               string
	RelationSource         *PrivateAgentContextRelationSource
	CanContributeContext   bool
	CanWake                bool
	CanShare               bool
}

type PrivateAgentMentionCandidate struct {
	ID           string
	Name         string
	DisplayName  string
	MemberID     string
	AvatarURL    string
	OwnerProfile *TaskPrincipalProfile
	// OwnerMemberID/OwnerUserID/OwnerName/OwnerDisplayName are legacy
	// compatibility aliases. New runtime/API/UI code must derive owner
	// identity from OwnerProfile.
	OwnerMemberID    string
	OwnerUserID      string
	OwnerName        string
	OwnerDisplayName string
	Relation         string
	RelationSource   *PrivateAgentContextRelationSource
}

type ListPrivateAgentMentionCandidatesRequest struct {
	Context ServiceContext
	Scope   TaskCollaboratorScopeRef
	Prefix  string
	Limit   int
	// CandidateScanLimit bounds pre-authorization overfetch so unauthorized
	// candidates cannot force an unbounded scan before final filtering.
	CandidateScanLimit int
}

type ListPrivateAgentMentionCandidatesResponse struct {
	Candidates []PrivateAgentMentionCandidate
}

type ListTaskFieldOptionsRequest struct {
	Context ServiceContext
}

type ListTaskFieldOptionsResponse struct {
	CanManage       bool
	PriorityOptions []TaskFieldOption
	KindOptions     []TaskFieldOption
}

type TaskMemberOption struct {
	MemberID string
	Type     string
	UserID   string
	AgentID  string
	Name     string
	Label    string
}

type ListTaskOptionsRequest struct {
	Context ServiceContext
	Board   BoardRef
	Role    string
	Query   string
	Limit   int
	Cursor  string
}

type ListTaskOptionsResponse struct {
	PriorityOptions []TaskFieldOption
	KindOptions     []TaskFieldOption
	AssigneeOptions []TaskMemberOption
	Limit           int
	HasMore         bool
	NextCursor      string
}

type TaskBoardViewState struct {
	View                string
	SearchQuery         string
	QuickFilters        []string
	StructuredFilters   map[string][]string
	SortMode            string
	CardPropertyToggles []string
	DoneCollapsed       bool
}

type GetTaskBoardViewStateRequest struct {
	Context ServiceContext
	Board   BoardRef
}

type GetTaskBoardViewStateResponse struct {
	ViewState TaskBoardViewState
}

type PutTaskBoardViewStateRequest struct {
	Context   ServiceContext
	Board     BoardRef
	ViewState TaskBoardViewState
}

type PutTaskBoardViewStateResponse struct {
	ViewState TaskBoardViewState
}

type TaskBoardSavedView struct {
	ID          string
	WorkspaceID string
	MemberID    string
	Name        string
	ViewState   TaskBoardViewState
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

type ListTaskBoardSavedViewsRequest struct {
	Context ServiceContext
}

type ListTaskBoardSavedViewsResponse struct {
	SavedViews []TaskBoardSavedView
}

type CreateTaskBoardSavedViewRequest struct {
	Context   ServiceContext
	Name      string
	ViewState TaskBoardViewState
}

type CreateTaskBoardSavedViewResponse struct {
	SavedView TaskBoardSavedView
}

type UpdateTaskBoardSavedViewRequest struct {
	Context     ServiceContext
	SavedViewID string
	Name        string
	ViewState   TaskBoardViewState
}

type UpdateTaskBoardSavedViewResponse struct {
	SavedView TaskBoardSavedView
}

type DeleteTaskBoardSavedViewRequest struct {
	Context     ServiceContext
	SavedViewID string
}

type DeleteTaskBoardSavedViewResponse struct{}

type TaskFieldOptionInput struct {
	FieldKind string
	Key       string
	Label     string
	Aliases   []string
}

type UpsertTaskFieldOptionRequest struct {
	Context  ServiceContext
	OptionID string
	Option   TaskFieldOptionInput
}

type UpsertTaskFieldOptionResponse struct {
	Option        TaskFieldOption
	AffectedTasks []Task
}

type ReorderTaskFieldOptionRequest struct {
	Context  ServiceContext
	OptionID string
	Position int64
}

type ReorderTaskFieldOptionResponse struct {
	Option TaskFieldOption
}

type SetTaskFieldOptionActiveRequest struct {
	Context  ServiceContext
	OptionID string
	Active   bool
}

type SetTaskFieldOptionActiveResponse struct {
	Option        TaskFieldOption
	AffectedTasks []Task
}

type CreateTaskMetadata struct {
	SourceThreadChannelID       string
	SourceThreadParentChannelID string
	SourceThreadParentMessageID string
	SourceThreadRef             string
}

func normalizeCreateTaskMetadata(metadata *CreateTaskMetadata) (CreateTaskMetadata, error) {
	if metadata == nil {
		return CreateTaskMetadata{}, nil
	}

	normalized := CreateTaskMetadata{
		SourceThreadChannelID:       strings.TrimSpace(metadata.SourceThreadChannelID),
		SourceThreadParentChannelID: strings.TrimSpace(metadata.SourceThreadParentChannelID),
		SourceThreadParentMessageID: strings.TrimSpace(metadata.SourceThreadParentMessageID),
		SourceThreadRef:             strings.TrimSpace(metadata.SourceThreadRef),
	}

	hasSourceThreadParentChannel := normalized.SourceThreadParentChannelID != ""
	hasSourceThreadParentMessage := normalized.SourceThreadParentMessageID != ""
	if hasSourceThreadParentChannel != hasSourceThreadParentMessage {
		return CreateTaskMetadata{}, badRequest("sourceThreadParentChannelId and sourceThreadParentMessageId must both be set")
	}
	if normalized.SourceThreadRef != "" && (!hasSourceThreadParentChannel || !hasSourceThreadParentMessage) {
		return CreateTaskMetadata{}, badRequest("sourceThreadParentChannelId and sourceThreadParentMessageId are required when sourceThreadRef is set")
	}
	if normalized.SourceThreadChannelID != "" && (!hasSourceThreadParentChannel || !hasSourceThreadParentMessage) {
		return CreateTaskMetadata{}, badRequest("sourceThreadChannelId requires sourceThreadParentChannelId and sourceThreadParentMessageId")
	}
	if !hasSourceThreadParentChannel {
		normalized.SourceThreadChannelID = ""
		normalized.SourceThreadRef = ""
	}

	return normalized, nil
}

type TaskCreateInput struct {
	Title            string
	Description      string
	Kind             string
	Priority         string
	KindOptionID     string
	PriorityOptionID string
	AssigneeRef      string
}

type CreateTasksRequest struct {
	Context  ServiceContext
	Board    BoardRef
	Titles   []string
	Task     *TaskCreateInput
	Metadata *CreateTaskMetadata
}

type CreateTasksResponse struct {
	ChannelID    string
	Tasks        []Task
	CreatedTasks []CreatedTask
}

type CreateSubtasksRequest struct {
	Context         ServiceContext
	Board           BoardRef
	Task            TaskRef
	ParentTaskScope *domain.MessageScopeValue
	SourceChannelID string
	Metadata        *CreateTaskMetadata
	Titles          []string
	Input           *TaskCreateInput
}

type CreateSubtasksResponse struct {
	ChannelID    string
	Tasks        []Task
	CreatedTasks []CreatedTask
}

type ClaimTasksRequest struct {
	Context ServiceContext
	Board   BoardRef
	Tasks   []TaskRef
}

type ClaimTasksResponse struct {
	ChannelID          string
	Task               Task
	Tasks              []Task
	Results            []ClaimResult
	ChangedTaskNumbers []int64
}

type UnclaimTaskRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
}

type UnclaimTaskResponse struct {
	Task Task
}

type TaskBlockInput struct {
	Reason string
}

type BlockTaskRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Block   TaskBlockInput
}

type BlockTaskResponse struct {
	Task    Task
	Changed bool
}

type UnblockTaskRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
}

type UnblockTaskResponse struct {
	Task    Task
	Changed bool
}

type DeleteTaskRequest struct {
	Context     ServiceContext
	Board       BoardRef
	Task        TaskRef
	SubtaskMode DeleteTaskSubtaskMode
}

type DeleteTaskResponse struct {
	ChannelID           string
	DeletedTaskIDs      []string
	DeletedTaskNumbers  []int64
	PromotedTasks       []Task
	PromotedTaskNumbers []int64
}

type GetTaskDetailRequest struct {
	Context                 ServiceContext
	Board                   BoardRef
	Task                    TaskRef
	SkipDiscussionReactions bool
	SkipTimeline            bool
	TaskOnly                bool
}

type GetTaskDetailResponse struct {
	Detail TaskDetail
}

// GetReadableTaskScopeRequest asks for the narrow authorization projection
// needed by Task-adjacent paginated resources.
type GetReadableTaskScopeRequest struct {
	Context ServiceContext
	Task    TaskRef
}

// GetReadableTaskScopeResponse proves Task readability and returns only the
// canonical Task scope identifier needed by the adjacent resource.
type GetReadableTaskScopeResponse struct {
	TaskID      string
	TaskScopeID string
}

type GetTaskTimelineRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Cursor  TaskTimelineCursor
}

type GetTaskTimelineResponse struct {
	Entries    []TaskTimelineEntry
	HasMore    bool
	NextCursor TaskTimelineCursor
}

type TaskTimelinePage = GetTaskTimelineResponse

type MarkTaskReadRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
}

type MarkTaskReadResponse struct {
	Task      Task
	ReadState TaskReadState
}

type RelatedTaskSignal struct {
	Type      string
	Payload   string
	CreatedAt time.Time
}

type RelatedTaskUnread struct {
	Count        int64
	ManualUnread bool
}

type RelatedTaskRow struct {
	TaskID          string
	TaskScopeID     string
	ChannelID       string
	TaskNumber      int64
	Title           string
	Status          string
	ParentTaskID    string
	ParentTaskRef   string
	Source          message.ContributionSource
	SourceThread    *TaskSourceThread
	AssigneeProfile *TaskPrincipalProfile
	LastSignal      *RelatedTaskSignal
	Unread          RelatedTaskUnread
	Route           string
}

type RelatedTasksResponse struct {
	Rows       []RelatedTaskRow
	HasMore    bool
	NextCursor RelatedTaskCursor
}

type RelatedTaskCursor struct {
	TaskNumber int64
	TaskID     string
}

func (cursor RelatedTaskCursor) IsZero() bool {
	return cursor.TaskNumber <= 0 && strings.TrimSpace(cursor.TaskID) == ""
}

type RelatedTaskStatus string

const (
	RelatedTaskStatusActive RelatedTaskStatus = "active"
	RelatedTaskStatusDone   RelatedTaskStatus = "done"
)

type RelatedTaskUnreadSummary struct {
	TaskCount    int64
	Unread       RelatedTaskUnread
	StatusCounts map[string]int64
}

type RelatedTaskPage struct {
	Limit  int
	Cursor RelatedTaskCursor
	Status RelatedTaskStatus
}

type ListRelatedTasksForThreadRequest struct {
	Context         ServiceContext
	ThreadChannelID string
	Limit           int
	Cursor          RelatedTaskCursor
	Status          RelatedTaskStatus
}

type ListRelatedExecutionTasksForAgentRequest struct {
	Context ServiceContext
	AgentID string
	Limit   int
	Cursor  RelatedTaskCursor
	Status  RelatedTaskStatus
}

type RelatedExecutionTasksSummaryForAgentRequest struct {
	Context ServiceContext
	AgentID string
	Limit   int
	Cursor  RelatedTaskCursor
	Status  RelatedTaskStatus
}

type RelatedTasksSummaryForThreadRequest struct {
	Context         ServiceContext
	ThreadChannelID string
}

type TaskCheckpointInput struct {
	Text string
}

type AddTaskCheckpointRequest struct {
	Context    ServiceContext
	Board      BoardRef
	Task       TaskRef
	Checkpoint TaskCheckpointInput
}

type AddTaskCheckpointResponse struct {
	Task Task
}

type TaskScopeMessageInput struct {
	Content       string
	AttachmentIDs []string
	MentionHints  []TaskScopeMessageMentionHint
	// Metadata carries trusted message metadata supplied by internal writers.
	// Public Task message adapters construct this type without setting it.
	Metadata map[string]any
	// IdempotencyPayloadDigest is an internal trusted override for a command
	// whose persisted Task message contains server-derived materialization.
	// Public Task message adapters never expose or set it.
	IdempotencyPayloadDigest string
}

type TaskScopeMessageMentionHint struct {
	Type     string
	ID       string
	Token    string
	Metadata map[string]any
}

type SendTaskScopeMessageRequest struct {
	Context         ServiceContext
	Board           BoardRef
	Task            TaskRef
	TaskScopeID     string
	Message         TaskScopeMessageInput
	ClientMessageID string
	Freshness       message.SendMessageFreshness
}

type SendTaskScopeMessageResponse struct {
	Task                 Task
	Message              TaskDiscussionEntry
	State                string
	MessageID            string
	TargetKey            string
	ClientMessageID      string
	SeenUpToSeq          int64
	AcceptedSeq          int64
	LatestSeq            int64
	NeedsRefreshUntilSeq int64
}

// TaskUpdatePatch carries canonical task field mutations. Task discussion writes
// use SendTaskScopeMessage instead of a task-native discussion patch.
type TaskUpdatePatch struct {
	Title              *string
	Description        *string
	Kind               *string
	Priority           *string
	KindOptionID       *string
	PriorityOptionID   *string
	AssigneeRef        *string
	AcceptanceCriteria []TaskChecklistItem
	ReviewChecklist    []TaskChecklistItem
	PinnedMessageRefs  []string
	RelevantURLs       []string
	WorkspaceRefs      []string
	BlockedReason      *string
	ParentTaskRef      *string
	AttachmentIDs      []string
}

type UpdateTaskRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Patch   TaskUpdatePatch
}

type UpdateTaskResponse struct {
	Task Task
}

type UpdateTaskStatusRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Status  string
}

type UpdateTaskStatusResponse struct {
	Task    Task
	Changed bool
}

type TaskReviewRequestInput struct {
	ReviewerRef         string
	Summary             string
	Questions           []string
	ReviewTargetType    string
	ReviewTargetPayload string
}

type RequestTaskReviewRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Review  TaskReviewRequestInput
}

type RequestTaskReviewResponse struct {
	Task   Task
	Review TaskReviewRequest
}

type TaskReviewDecisionInput struct {
	ReviewID  string
	Decision  string
	Summary   string
	Questions []string
}

type TaskReviewReassignmentInput struct {
	ReviewID    string
	ReviewerRef string
}

type TaskReviewCancellationInput struct {
	ReviewID string
}

type ApproveTaskReviewRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Review  TaskReviewDecisionInput
}

type RequestTaskChangesRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Review  TaskReviewDecisionInput
}

type ReassignTaskReviewRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Review  TaskReviewReassignmentInput
}

type MarkTaskReviewNotReviewableRequest struct {
	Context ServiceContext
	Board   BoardRef
	Task    TaskRef
	Review  TaskReviewCancellationInput
}

type TaskReviewDecisionResponse struct {
	Task   Task
	Review TaskReviewRequest
}

type ListTaskReviewQueueRequest struct {
	Context ServiceContext
	Board   BoardRef
	Limit   int
	Cursor  string
}

type ListTaskReviewQueueResponse struct {
	Reviews    []TaskReviewRequest
	Limit      int
	HasMore    bool
	NextCursor string
}
