package taskboard

import (
	"context"
	"strings"
	"time"
)

const (
	taskBrowseDefaultLimit      = 20
	taskBrowseBoardDefaultLimit = 10
	taskBrowseMaxLimit          = 100
)

type TaskBrowseColumnKey string

const (
	TaskBrowseColumnTodo       TaskBrowseColumnKey = "todo"
	TaskBrowseColumnInProgress TaskBrowseColumnKey = "in_progress"
	TaskBrowseColumnInReview   TaskBrowseColumnKey = "in_review"
	TaskBrowseColumnDone       TaskBrowseColumnKey = "done"
)

var allowedTaskBrowseColumnKeys = map[string]struct{}{
	string(TaskBrowseColumnTodo):       {},
	string(TaskBrowseColumnInProgress): {},
	string(TaskBrowseColumnInReview):   {},
	string(TaskBrowseColumnDone):       {},
}

type TaskBrowseQuery struct {
	Limit             int
	SearchQuery       string
	SortMode          string
	StatusFilter      string
	QuickFilters      []string
	StructuredFilters map[string][]string
	IncludeSubtasks   bool
	Cursor            TaskBrowseCursor
	ColumnCursors     map[string]TaskBrowseCursor
}

type TaskBrowseCursor struct {
	SortMode        string
	TaskID          string
	CreatedAt       time.Time
	ActivityAt      time.Time
	Title           string
	PriorityMissing bool
	PriorityLabel   string
	TaskNumber      int64
}

func (cursor TaskBrowseCursor) IsZero() bool {
	return cursor == (TaskBrowseCursor{})
}

type BrowseWorkspaceTasksRequest struct {
	Context ServiceContext
	Query   TaskBrowseQuery
}

type BrowseWorkspaceTaskBoardRequest struct {
	Context ServiceContext
	Query   TaskBrowseQuery
}

type BrowseChannelTasksRequest struct {
	Context ServiceContext
	Board   BoardRef
	Query   TaskBrowseQuery
}

type BrowseTaskBoardRequest struct {
	Context ServiceContext
	Board   BoardRef
	Query   TaskBrowseQuery
}

type TaskRowBrowsePage struct {
	QueryEcho    TaskBrowseQuery
	Rows         []Task
	TotalCount   int64
	HasMore      bool
	NextCursor   TaskBrowseCursor
	ColumnCounts map[string]int64
}

type TaskBoardColumnPage struct {
	Tasks      []Task
	Count      int64
	HasMore    bool
	NextCursor TaskBrowseCursor
}

type TaskBoardColumnDefinition struct {
	Key    string
	Name   string
	Source string
}

type TaskBoardBrowsePage struct {
	QueryEcho         TaskBrowseQuery
	TotalCount        int64
	ColumnOrder       []string
	ColumnDefinitions []TaskBoardColumnDefinition
	ColumnCounts      map[string]int64
	Columns           map[string]TaskBoardColumnPage
}

func normalizeTaskBrowseQuery(input TaskBrowseQuery) TaskBrowseQuery {
	limit := input.Limit
	switch {
	case limit <= 0:
		limit = taskBrowseDefaultLimit
	case limit > taskBrowseMaxLimit:
		limit = taskBrowseMaxLimit
	}
	return TaskBrowseQuery{
		Limit:             limit,
		SearchQuery:       normalizeBrowseSearchQuery(input.SearchQuery),
		SortMode:          normalizeTaskBoardSortMode(input.SortMode),
		StatusFilter:      normalizeTaskBrowseStatusFilter(input.StatusFilter),
		QuickFilters:      normalizeTaskBoardViewStateList(input.QuickFilters, allowedTaskBoardQuickFilters),
		StructuredFilters: normalizeTaskBoardStructuredFilters(input.StructuredFilters),
		IncludeSubtasks:   input.IncludeSubtasks,
		Cursor:            normalizeTaskBrowseCursorForQuery(input.Cursor),
		ColumnCursors:     normalizeTaskBrowseColumnCursors(input.ColumnCursors),
	}
}

func normalizeTaskBrowseStatusFilter(status string) string {
	switch strings.TrimSpace(status) {
	case "", "all":
		return ""
	case "active":
		return "active"
	case string(StatusTodo):
		return string(StatusTodo)
	case string(StatusInProgress):
		return string(StatusInProgress)
	case string(StatusInReview):
		return string(StatusInReview)
	case string(StatusDone):
		return string(StatusDone)
	default:
		return strings.TrimSpace(status)
	}
}

func normalizeTaskBoardBrowseQuery(input TaskBrowseQuery) TaskBrowseQuery {
	normalized := normalizeTaskBrowseQuery(input)
	if input.Limit <= 0 {
		normalized.Limit = taskBrowseBoardDefaultLimit
	}
	return normalized
}

func normalizeTaskBrowseCursorForQuery(cursor TaskBrowseCursor) TaskBrowseCursor {
	if cursor.IsZero() {
		return TaskBrowseCursor{}
	}
	cursor.SortMode = normalizeTaskBoardSortMode(cursor.SortMode)
	cursor.TaskID = strings.TrimSpace(cursor.TaskID)
	cursor.Title = strings.TrimSpace(cursor.Title)
	cursor.PriorityLabel = strings.TrimSpace(cursor.PriorityLabel)
	if !cursor.CreatedAt.IsZero() {
		cursor.CreatedAt = cursor.CreatedAt.UTC()
	}
	if !cursor.ActivityAt.IsZero() {
		cursor.ActivityAt = cursor.ActivityAt.UTC()
	}
	return cursor
}

func normalizeTaskBrowseColumnCursors(input map[string]TaskBrowseCursor) map[string]TaskBrowseCursor {
	if len(input) == 0 {
		return nil
	}
	normalized := make(map[string]TaskBrowseCursor, len(input))
	for key, cursor := range input {
		trimmedKey := strings.TrimSpace(key)
		if trimmedKey == "" {
			continue
		}
		normalizedCursor := normalizeTaskBrowseCursorForQuery(cursor)
		if normalizedCursor.IsZero() {
			continue
		}
		normalized[trimmedKey] = normalizedCursor
	}
	if len(normalized) == 0 {
		return nil
	}
	return normalized
}

func (txs TxStore) BrowseWorkspaceTasks(ctx context.Context, request BrowseWorkspaceTasksRequest) (TaskRowBrowsePage, error) {
	if _, err := request.Context.requireWorkspaceID(); err != nil {
		return TaskRowBrowsePage{}, err
	}
	userID, _, err := request.Context.requireUser()
	if err != nil {
		return TaskRowBrowsePage{}, err
	}
	query := normalizeTaskBrowseQuery(request.Query)
	if _, err := normalizeTaskBrowseCursor(query.Cursor, query.SortMode); err != nil {
		return TaskRowBrowsePage{}, err
	}
	return txs.BrowseWorkspaceRowsForUser(ctx, userID, request.Context.WorkspaceID, query)
}

func (txs TxStore) BrowseWorkspaceTaskBoard(ctx context.Context, request BrowseWorkspaceTaskBoardRequest) (TaskBoardBrowsePage, error) {
	if _, err := request.Context.requireWorkspaceID(); err != nil {
		return TaskBoardBrowsePage{}, err
	}
	userID, _, err := request.Context.requireUser()
	if err != nil {
		return TaskBoardBrowsePage{}, err
	}
	if !request.Query.Cursor.IsZero() {
		return TaskBoardBrowsePage{}, badRequest("board browse does not support cursor")
	}
	query := normalizeTaskBoardBrowseQuery(request.Query)
	return txs.BrowseWorkspaceBoardForUser(
		ctx,
		userID,
		request.Context.WorkspaceID,
		query,
		query.ColumnCursors,
		request.Query.Limit <= 0 && request.Query.ColumnCursors[string(TaskBrowseColumnDone)].IsZero(),
	)
}

func (txs TxStore) BrowseChannelTasks(ctx context.Context, request BrowseChannelTasksRequest) (TaskRowBrowsePage, error) {
	if _, err := request.Context.requireWorkspaceID(); err != nil {
		return TaskRowBrowsePage{}, err
	}
	query := normalizeTaskBrowseQuery(request.Query)
	if _, err := normalizeTaskBrowseCursor(query.Cursor, query.SortMode); err != nil {
		return TaskRowBrowsePage{}, err
	}
	switch request.Context.principalType() {
	case PrincipalTypeUser:
		userID, _, err := request.Context.requireUser()
		if err != nil {
			return TaskRowBrowsePage{}, err
		}
		if _, err := request.Board.requireChannelID(); err != nil {
			return TaskRowBrowsePage{}, err
		}
		return txs.BrowseChannelRowsForUser(ctx, userID, request.Context.WorkspaceID, request.Board.ChannelID, query)
	case PrincipalTypeAgent:
		agent, err := request.Context.requireAgentActor()
		if err != nil {
			return TaskRowBrowsePage{}, err
		}
		return txs.BrowseChannelRowsForAgent(ctx, agent, request.Board, query)
	default:
		return TaskRowBrowsePage{}, badRequest("context subject principalType is required")
	}
}

func (txs TxStore) BrowseTaskBoard(ctx context.Context, request BrowseTaskBoardRequest) (TaskBoardBrowsePage, error) {
	if _, err := request.Context.requireWorkspaceID(); err != nil {
		return TaskBoardBrowsePage{}, err
	}
	userID, _, err := request.Context.requireUser()
	if err != nil {
		return TaskBoardBrowsePage{}, err
	}
	if _, err := request.Board.requireChannelID(); err != nil {
		return TaskBoardBrowsePage{}, err
	}
	if !request.Query.Cursor.IsZero() {
		return TaskBoardBrowsePage{}, badRequest("board browse does not support cursor")
	}
	query := normalizeTaskBoardBrowseQuery(request.Query)
	return txs.BrowseChannelBoardForUser(
		ctx,
		userID,
		request.Context.WorkspaceID,
		request.Board.ChannelID,
		query,
		query.ColumnCursors,
		request.Query.Limit <= 0 && request.Query.ColumnCursors[string(TaskBrowseColumnDone)].IsZero(),
	)
}
