package taskboard

import (
	"context"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/tasktimeline"
)

func createTaskInputs(task *TaskCreateInput, titles []string) ([]TaskCreateInput, error) {
	if task != nil && len(titles) > 0 {
		return nil, badRequest("task and titles cannot both be set")
	}
	if task != nil {
		return []TaskCreateInput{*task}, nil
	}
	if len(titles) == 0 {
		return nil, badRequest("at least one task is required")
	}
	inputs := make([]TaskCreateInput, 0, len(titles))
	for _, title := range titles {
		inputs = append(inputs, TaskCreateInput{Title: title})
	}
	return inputs, nil
}

func createInputOptionalString(value string) *string {
	trimmed := strings.TrimSpace(value)
	if trimmed == "" {
		return nil
	}
	return &trimmed
}

func (txs TxStore) resolveTaskInsertInput(ctx context.Context, tx coredb.Tx, workspaceID string, channelID string, actorMemberID string, input TaskCreateInput, metadata CreateTaskMetadata, strictFieldOptions bool) (taskInsertInput, error) {
	trimmedTitle := strings.TrimSpace(input.Title)
	if trimmedTitle == "" {
		return taskInsertInput{}, badRequest("task title is required")
	}

	assigneeMemberID, err := resolveOptionalTaskAssigneeRef(ctx, tx, workspaceID, channelID, actorMemberID, input.AssigneeRef)
	if err != nil {
		return taskInsertInput{}, err
	}
	kindUpdate, err := txs.resolveTaskFieldUpdate(ctx, tx, workspaceID, taskFieldKindKind, createInputOptionalString(input.KindOptionID), createInputOptionalString(input.Kind), strictFieldOptions)
	if err != nil {
		return taskInsertInput{}, err
	}
	priorityUpdate, err := txs.resolveTaskFieldUpdate(ctx, tx, workspaceID, taskFieldKindPriority, createInputOptionalString(input.PriorityOptionID), createInputOptionalString(input.Priority), strictFieldOptions)
	if err != nil {
		return taskInsertInput{}, err
	}

	return taskInsertInput{
		Title:            trimmedTitle,
		Description:      strings.TrimSpace(input.Description),
		AssigneeMemberID: assigneeMemberID,
		KindUpdate:       kindUpdate,
		PriorityUpdate:   priorityUpdate,
		Metadata:         metadata,
	}, nil
}

func (txs TxStore) createTasksInTx(ctx context.Context, tx coredb.Tx, workspaceID string, channelID string, actorMemberID string, inputs []TaskCreateInput, metadata CreateTaskMetadata, strictFieldOptions bool, fencer taskAuthorityFencer) ([]string, []CreatedTask, error) {
	if len(inputs) == 0 {
		return nil, nil, badRequest("at least one task is required")
	}

	resolvedInputs := make([]taskInsertInput, 0, len(inputs))
	for _, input := range inputs {
		resolved, err := txs.resolveTaskInsertInput(ctx, tx, workspaceID, channelID, actorMemberID, input, metadata, strictFieldOptions)
		if err != nil {
			return nil, nil, err
		}
		resolvedInputs = append(resolvedInputs, resolved)
	}

	board, err := txs.ensureTaskBoard(ctx, tx, workspaceID, channelID)
	if err != nil {
		return nil, nil, err
	}
	// TOCTOU fence: the actor's active authority chain on the authoritative board
	// Channel must be revalidated under lock inside this transaction, before any
	// Task/event/scope/participant row is inserted. The Channel is derived
	// server-side from the locked task_boards row (never caller-reported).
	if _, err := fencer.lockBoardChannelForCreate(ctx, tx, workspaceID, board.ID); err != nil {
		return nil, nil, err
	}
	nextTaskNumber, err := txs.allocateTaskNumbers(ctx, tx, board, len(resolvedInputs))
	if err != nil {
		return nil, nil, err
	}

	taskIDs := make([]string, 0, len(resolvedInputs))
	createdTasks := make([]CreatedTask, 0, len(resolvedInputs))
	now := time.Now().UTC()
	for index, input := range resolvedInputs {
		taskNumber := nextTaskNumber + int64(index)
		taskID, taskScopeID, err := txs.insertTask(ctx, tx, board, taskNumber, actorMemberID, input, now)
		if err != nil {
			return nil, nil, err
		}
		if err := ensureTaskScopeParticipantsDefaultUnwatched(ctx, tx, taskScopeID, []string{input.AssigneeMemberID}, now); err != nil {
			return nil, nil, err
		}
		activityIntents := []tasktimeline.TypedActivityIntent{
			taskTypedEventIntent(taskID, actorMemberID, taskEventCreated, "", now),
		}
		if input.AssigneeMemberID != "" {
			activityIntents = append(activityIntents, taskTypedEventIntent(taskID, actorMemberID, taskEventAssigned, input.AssigneeMemberID, now))
		}
		if err := applyTaskTypedEventPlan(ctx, tx, activityIntents...); err != nil {
			return nil, nil, err
		}
		taskIDs = append(taskIDs, taskID)
		createdTasks = append(createdTasks, CreatedTask{TaskScopeID: taskScopeID, TaskNumber: taskNumber, Title: input.Title})
	}
	return taskIDs, createdTasks, nil
}
