package taskboard

import (
	"context"
	"strings"
)

func (txs TxStore) ListTasks(ctx context.Context, request ListTasksRequest) (ListTasksResponse, error) {
	workspaceID, err := request.Context.requireWorkspaceID()
	if err != nil {
		return ListTasksResponse{}, err
	}

	switch request.Context.principalType() {
	case PrincipalTypeUser:
		userID, _, err := request.Context.requireUser()
		if err != nil {
			return ListTasksResponse{}, err
		}
		channelID, err := request.Board.requireChannelID()
		if err != nil {
			return ListTasksResponse{}, err
		}
		tasks, err := txs.ListForUser(ctx, userID, workspaceID, channelID, request.IncludeSubtasks)
		if err != nil {
			return ListTasksResponse{}, err
		}
		statusFilter, err := ParseStatusFilter(request.Status)
		if err != nil {
			return ListTasksResponse{}, err
		}
		if statusFilter != "" {
			tasks = filterTasksByStatus(tasks, statusFilter)
		}
		return ListTasksResponse{
			ChannelID: channelID,
			Tasks:     tasks,
		}, nil
	case PrincipalTypeAgent:
		agent, err := request.Context.requireAgentActor()
		if err != nil {
			return ListTasksResponse{}, err
		}
		query := normalizeTaskBrowseQuery(TaskBrowseQuery{
			StatusFilter:    request.Status,
			IncludeSubtasks: request.IncludeSubtasks,
		})
		channelID, err := txs.resolveAccessibleAgentBoard(ctx, txs.db, agent, request.Board)
		if err != nil {
			return ListTasksResponse{}, err
		}
		page, err := txs.browseChannelRowsForAgentChannelID(ctx, agent, channelID, query)
		if err != nil {
			return ListTasksResponse{}, err
		}
		return ListTasksResponse{
			ChannelID:  channelID,
			Tasks:      page.Rows,
			TaskViews:  taskViewsFromTasks(page.Rows),
			Limit:      page.QueryEcho.Limit,
			TotalCount: page.TotalCount,
			HasMore:    page.HasMore,
			NextCursor: page.NextCursor,
		}, nil
	default:
		return ListTasksResponse{}, badRequest("context subject principalType is required")
	}
}

func taskViewsFromTasks(tasks []Task) []TaskView {
	if len(tasks) == 0 {
		return nil
	}
	views := make([]TaskView, 0, len(tasks))
	for _, task := range tasks {
		views = append(views, TaskView{
			TaskScopeID:      task.TaskScopeID,
			TaskNumber:       task.TaskNumber,
			Title:            task.Title,
			Status:           task.Status,
			AssigneeProfile:  task.AssigneeProfile,
			CreatedByProfile: task.CreatedByProfile,
			CreatedByName:    task.CreatedByName,
		})
	}
	return views
}

func (txs TxStore) ListWorkspaceTasks(ctx context.Context, request ListWorkspaceTasksRequest) (ListWorkspaceTasksResponse, error) {
	workspaceID, err := request.Context.requireWorkspaceID()
	if err != nil {
		return ListWorkspaceTasksResponse{}, err
	}

	switch request.Context.principalType() {
	case PrincipalTypeUser:
		userID, _, err := request.Context.requireUser()
		if err != nil {
			return ListWorkspaceTasksResponse{}, err
		}
		tasks, err := txs.ListWorkspaceTasksForUser(ctx, userID, workspaceID, request.IncludeSubtasks)
		if err != nil {
			return ListWorkspaceTasksResponse{}, err
		}
		statusFilter, err := ParseStatusFilter(request.Status)
		if err != nil {
			return ListWorkspaceTasksResponse{}, err
		}
		if statusFilter != "" {
			tasks = filterTasksByStatus(tasks, statusFilter)
		}
		return ListWorkspaceTasksResponse{Tasks: tasks}, nil
	case PrincipalTypeAgent:
		return ListWorkspaceTasksResponse{}, badRequest("workspace task hub does not support agent principals")
	default:
		return ListWorkspaceTasksResponse{}, badRequest("context subject principalType is required")
	}
}

func (txs TxStore) CreateTasks(ctx context.Context, request CreateTasksRequest) (CreateTasksResponse, error) {
	workspaceID, err := request.Context.requireWorkspaceID()
	if err != nil {
		return CreateTasksResponse{}, err
	}
	metadata, err := normalizeCreateTaskMetadata(request.Metadata)
	if err != nil {
		return CreateTasksResponse{}, err
	}
	inputs, err := createTaskInputs(request.Task, request.Titles)
	if err != nil {
		return CreateTasksResponse{}, err
	}
	if err := validateTaskCreateInputs(inputs); err != nil {
		return CreateTasksResponse{}, err
	}
	switch request.Context.principalType() {
	case PrincipalTypeUser:
		userID, displayName, err := request.Context.requireUser()
		if err != nil {
			return CreateTasksResponse{}, err
		}
		channelID, err := request.Board.requireChannelID()
		if err != nil {
			return CreateTasksResponse{}, err
		}
		tasks, createdTasks, err := txs.CreateForUser(ctx, userID, displayName, workspaceID, channelID, inputs, metadata)
		if err != nil {
			return CreateTasksResponse{}, err
		}
		return CreateTasksResponse{
			ChannelID:    channelID,
			Tasks:        tasks,
			CreatedTasks: createdTasks,
		}, nil
	case PrincipalTypeAgent:
		agent, err := request.Context.requireAgentActor()
		if err != nil {
			return CreateTasksResponse{}, err
		}
		channelID, createdTasks, err := txs.CreateForAgent(ctx, agent, request.Board, inputs, metadata)
		if err != nil {
			return CreateTasksResponse{}, err
		}
		tasks, err := txs.ListByTaskNumbers(ctx, workspaceID, channelID, createdTaskNumbers(createdTasks))
		if err != nil {
			return CreateTasksResponse{}, err
		}
		return CreateTasksResponse{
			ChannelID:    channelID,
			Tasks:        tasks,
			CreatedTasks: createdTasks,
		}, nil
	default:
		return CreateTasksResponse{}, badRequest("context subject principalType is required")
	}
}

func (txs TxStore) ClaimTasks(ctx context.Context, request ClaimTasksRequest) (ClaimTasksResponse, error) {
	workspaceID, err := request.Context.requireWorkspaceID()
	if err != nil {
		return ClaimTasksResponse{}, err
	}

	switch request.Context.principalType() {
	case PrincipalTypeUser:
		if len(request.Tasks) != 1 {
			return ClaimTasksResponse{}, badRequest("exactly one task is required")
		}
		userID, _, err := request.Context.requireUser()
		if err != nil {
			return ClaimTasksResponse{}, err
		}
		taskID, err := request.Tasks[0].requireTaskID()
		if err != nil {
			return ClaimTasksResponse{}, err
		}
		task, err := txs.ClaimForUser(ctx, userID, workspaceID, taskID)
		if err != nil {
			return ClaimTasksResponse{}, err
		}
		return ClaimTasksResponse{
			ChannelID:          task.ChannelID,
			Task:               task,
			Tasks:              []Task{task},
			ChangedTaskNumbers: []int64{task.TaskNumber},
		}, nil
	case PrincipalTypeAgent:
		agent, err := request.Context.requireAgentActor()
		if err != nil {
			return ClaimTasksResponse{}, err
		}
		taskNumbers, err := taskNumbersFromRefs(request.Tasks)
		if err != nil {
			return ClaimTasksResponse{}, err
		}
		channelID, results, changedTaskNumbers, err := txs.ClaimForAgent(ctx, agent, request.Board, taskNumbers)
		if err != nil {
			return ClaimTasksResponse{}, err
		}
		tasks, err := txs.ListByTaskNumbers(ctx, workspaceID, channelID, changedTaskNumbers)
		if err != nil {
			return ClaimTasksResponse{}, err
		}
		return ClaimTasksResponse{
			ChannelID:          channelID,
			Tasks:              tasks,
			Results:            results,
			ChangedTaskNumbers: changedTaskNumbers,
		}, nil
	default:
		return ClaimTasksResponse{}, badRequest("context subject principalType is required")
	}
}

func (txs TxStore) UnclaimTask(ctx context.Context, request UnclaimTaskRequest) (UnclaimTaskResponse, error) {
	workspaceID, err := request.Context.requireWorkspaceID()
	if err != nil {
		return UnclaimTaskResponse{}, err
	}

	switch request.Context.principalType() {
	case PrincipalTypeUser:
		userID, _, err := request.Context.requireUser()
		if err != nil {
			return UnclaimTaskResponse{}, err
		}
		taskID, err := request.Task.requireTaskID()
		if err != nil {
			return UnclaimTaskResponse{}, err
		}
		task, err := txs.UnclaimForUser(ctx, userID, workspaceID, taskID)
		if err != nil {
			return UnclaimTaskResponse{}, err
		}
		return UnclaimTaskResponse{Task: task}, nil
	case PrincipalTypeAgent:
		agent, err := request.Context.requireAgentActor()
		if err != nil {
			return UnclaimTaskResponse{}, err
		}
		taskNumber, err := request.Task.requireTaskNumber()
		if err != nil {
			return UnclaimTaskResponse{}, err
		}
		task, err := txs.UnclaimForAgent(ctx, agent, request.Board, taskNumber)
		if err != nil {
			return UnclaimTaskResponse{}, err
		}
		return UnclaimTaskResponse{Task: task}, nil
	default:
		return UnclaimTaskResponse{}, badRequest("context subject principalType is required")
	}
}

func (txs TxStore) DeleteTask(ctx context.Context, request DeleteTaskRequest) (DeleteTaskResponse, error) {
	workspaceID, err := request.Context.requireWorkspaceID()
	if err != nil {
		return DeleteTaskResponse{}, err
	}
	switch request.Context.principalType() {
	case PrincipalTypeUser:
		userID, _, err := request.Context.requireUser()
		if err != nil {
			return DeleteTaskResponse{}, err
		}
		taskID, err := request.Task.requireTaskID()
		if err != nil {
			return DeleteTaskResponse{}, err
		}
		return txs.DeleteForUser(ctx, DeleteForUserRequest{
			UserID:      userID,
			WorkspaceID: workspaceID,
			TaskID:      taskID,
			SubtaskMode: request.SubtaskMode,
		})
	case PrincipalTypeAgent:
		agent, err := request.Context.requireAgentActor()
		if err != nil {
			return DeleteTaskResponse{}, err
		}
		taskNumber, err := request.Task.requireTaskNumber()
		if err != nil {
			return DeleteTaskResponse{}, err
		}
		return txs.DeleteForAgent(ctx, DeleteForAgentRequest{
			Agent:       agent,
			Board:       request.Board,
			TaskNumber:  taskNumber,
			SubtaskMode: request.SubtaskMode,
		})
	default:
		return DeleteTaskResponse{}, badRequest("context subject principalType is required")
	}
}

func filterTasksByStatus(tasks []Task, status string) []Task {
	if strings.TrimSpace(status) == "" {
		return tasks
	}
	filtered := make([]Task, 0, len(tasks))
	for _, task := range tasks {
		if task.Status == status {
			filtered = append(filtered, task)
		}
	}
	return filtered
}
