package taskboard

import (
	"fmt"
	"strings"
	"time"

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

type taskScanRow struct {
	task Task

	description         string
	kindOptionID        string
	kindOptionKey       string
	kindOptionLabel     string
	kindRawValue        string
	priorityOptionID    string
	priorityOptionKey   string
	priorityOptionLabel string
	priorityRawValue    string
	blockedReason       string
	lastActivityAt      time.Time
	artifactCount       int64
	checkpointTotal     int64
	checkpointDone      int64
	subtaskTotal        int64
	subtaskDone         int64
	discussionCount     int64
	parentTaskRef       string

	sourceThreadChannelID       string
	sourceThreadParentChannelID string
	sourceThreadParentMessageID string
	sourceThreadRef             string
	createdProjection           taskActorProjection
	assigneeProjection          taskActorProjection
}

func scanTask(scanner interface{ Scan(...any) error }) (Task, error) {
	var row taskScanRow
	if err := scanner.Scan(row.destinations()...); err != nil {
		if coredb.IsNotFound(err) {
			return Task{}, ErrNotFound
		}
		return Task{}, fmt.Errorf("scan task: %w", err)
	}
	return row.toTask()
}

func (row *taskScanRow) destinations() []any {
	// Keep this order aligned with taskSelectColumns.
	destinations := []any{
		&row.task.ID,
		&row.task.TaskScopeID,
		&row.task.ChannelID,
		&row.task.TaskNumber,
		&row.task.Title,
		&row.task.Status,
		&row.description,
		&row.kindOptionID,
		&row.kindOptionKey,
		&row.kindOptionLabel,
		&row.kindRawValue,
		&row.priorityOptionID,
		&row.priorityOptionKey,
		&row.priorityOptionLabel,
		&row.priorityRawValue,
		&row.blockedReason,
		&row.lastActivityAt,
		&row.artifactCount,
		&row.checkpointTotal,
		&row.checkpointDone,
		&row.subtaskTotal,
		&row.subtaskDone,
		&row.discussionCount,
		&row.parentTaskRef,
		&row.task.CreatedAt,
		&row.task.AssignedAt,
		&row.task.CompletedAt,
		&row.sourceThreadChannelID,
		&row.sourceThreadParentChannelID,
		&row.sourceThreadParentMessageID,
		&row.sourceThreadRef,
	}
	destinations = appendTaskActorProjectionDestinations(destinations, &row.createdProjection)
	return appendTaskActorProjectionDestinations(destinations, &row.assigneeProjection)
}

func (row taskScanRow) toTask() (Task, error) {
	task := row.task
	task.Description = row.description
	task.BlockedReason = row.blockedReason
	task.ParentTaskRef = row.parentTaskRef
	task.ArtifactCount = row.artifactCount
	task.DiscussionCount = row.discussionCount
	task.KindField = newTaskFieldValue(row.kindOptionID, row.kindOptionKey, row.kindOptionLabel, row.kindRawValue)
	task.PriorityField = newTaskFieldValue(row.priorityOptionID, row.priorityOptionKey, row.priorityOptionLabel, row.priorityRawValue)
	task.Kind = taskFieldDisplay(task.KindField)
	task.Priority = taskFieldDisplay(task.PriorityField)
	task.CheckpointProgress = TaskProgressSummary{
		Total: row.checkpointTotal,
		Done:  row.checkpointDone,
	}
	task.SubtaskProgress = TaskProgressSummary{
		Total: row.subtaskTotal,
		Done:  row.subtaskDone,
	}
	if !row.lastActivityAt.IsZero() {
		task.LastActivityAt = &row.lastActivityAt
	}
	if err := row.applyPrincipalProfiles(&task); err != nil {
		return Task{}, err
	}
	if strings.TrimSpace(row.sourceThreadParentChannelID) != "" && strings.TrimSpace(row.sourceThreadParentMessageID) != "" {
		task.SourceThread = &TaskSourceThread{
			ThreadChannelID: strings.TrimSpace(row.sourceThreadChannelID),
			ParentChannelID: row.sourceThreadParentChannelID,
			ParentMessageID: row.sourceThreadParentMessageID,
			Ref:             strings.TrimSpace(row.sourceThreadRef),
		}
	}
	task.Source = taskSourceContributionSource(task.SourceThread, task.CreatedByProfile)
	return task, nil
}

func (row taskScanRow) applyPrincipalProfiles(task *Task) error {
	createdProfile, err := taskPrincipalProfileFromProjection("createdBy", row.createdProjection, true)
	if err != nil {
		return fmt.Errorf("project createdBy principal profile: %w", err)
	}
	task.CreatedByProfile = createdProfile
	task.CreatedByType, task.CreatedByID, task.CreatedByName = taskLegacyCreatedByFromPrincipalProfile(createdProfile, task.CreatedByType, task.CreatedByID, task.CreatedByName)

	assigneeProfile, err := taskPrincipalProfileFromProjection("assignee", row.assigneeProjection, false)
	if err != nil {
		return fmt.Errorf("project assignee principal profile: %w", err)
	}
	task.AssigneeProfile = assigneeProfile
	return nil
}

func newTaskFieldValue(optionID string, key string, label string, rawValue string) *TaskFieldValue {
	trimmedOptionID := strings.TrimSpace(optionID)
	trimmedKey := strings.TrimSpace(key)
	trimmedLabel := strings.TrimSpace(label)
	trimmedRawValue := strings.TrimSpace(rawValue)
	if trimmedOptionID != "" && (trimmedKey != "" || trimmedLabel != "") {
		if trimmedLabel == "" {
			trimmedLabel = trimmedKey
		}
		return &TaskFieldValue{
			OptionID: trimmedOptionID,
			Key:      trimmedKey,
			Label:    trimmedLabel,
		}
	}
	if trimmedRawValue != "" {
		return &TaskFieldValue{
			Key:      trimmedRawValue,
			Label:    trimmedRawValue,
			RawValue: trimmedRawValue,
			IsLegacy: true,
		}
	}
	return &TaskFieldValue{IsNone: true}
}

func taskFieldDisplay(field *TaskFieldValue) string {
	if field == nil || field.IsNone {
		return ""
	}
	if strings.TrimSpace(field.Label) != "" {
		return strings.TrimSpace(field.Label)
	}
	if strings.TrimSpace(field.RawValue) != "" {
		return strings.TrimSpace(field.RawValue)
	}
	return strings.TrimSpace(field.Key)
}
