// Package asynctask 定义业务异步任务的统一公开契约。
package asynctask

import (
	stderrors "errors"
	"reflect"
	"time"
)

const (
	// StatusPending 表示任务待处理。
	StatusPending = 1
	// StatusProcessing 表示任务处理中。
	StatusProcessing = 2
	// StatusCompleted 表示任务已到达业务处理终点。
	StatusCompleted = 3
	// StatusFailed 表示任务整体无法执行到业务终点。
	StatusFailed = 4
	// StatusCancelled 表示业务明确支持且已经取消任务。
	StatusCancelled = 5
)

var statusNames = map[int]string{
	StatusPending:    "待处理",
	StatusProcessing: "处理中",
	StatusCompleted:  "已完成",
	StatusFailed:     "已失败",
	StatusCancelled:  "已取消",
}

// Projection 是跨业务统一的任务公开投影。
type Projection struct {
	TaskID       string     `json:"task_id"`
	Status       int        `json:"status"`
	StatusName   string     `json:"status_name"`
	TotalCount   int        `json:"total_count"`
	SuccessCount int        `json:"success_count"`
	FailedCount  int        `json:"failed_count"`
	Progress     int        `json:"progress"`
	ErrorCode    string     `json:"error_code,omitempty"`
	ErrorSummary string     `json:"error_summary,omitempty"`
	StartedAt    *time.Time `json:"started_at,omitempty"`
	CompletedAt  *time.Time `json:"completed_at,omitempty"`
	UpdatedAt    time.Time  `json:"updated_at"`
}

// TerminalResult 是构造终态投影所需的业务结果。
type TerminalResult struct {
	TaskID       string
	Status       int
	TotalCount   int
	SuccessCount int
	FailedCount  int
	ErrorCode    string
	ErrorSummary string
	StartedAt    *time.Time
	CompletedAt  *time.Time
	UpdatedAt    time.Time
}

// StatusName 返回固定任务状态的中文名称。
func StatusName(status int) string {
	return statusNames[status]
}

// IsTerminal 判断状态是否为统一终态。
func IsTerminal(status int) bool {
	return status == StatusCompleted || status == StatusFailed || status == StatusCancelled
}

// NewTerminalProjection 校验终态守恒并构造公开投影。
func NewTerminalProjection(result TerminalResult) (Projection, error) {
	if !IsTerminal(result.Status) {
		return Projection{}, stderrors.New("任务状态不是终态")
	}
	if result.TotalCount < 0 || result.SuccessCount < 0 || result.FailedCount < 0 {
		return Projection{}, stderrors.New("任务结果计数不能为负数")
	}
	if result.Status == StatusCompleted && result.TotalCount != result.SuccessCount+result.FailedCount {
		return Projection{}, stderrors.New("已完成任务的结果计数不守恒")
	}
	return Projection{
		TaskID: result.TaskID, Status: result.Status, StatusName: StatusName(result.Status),
		TotalCount: result.TotalCount, SuccessCount: result.SuccessCount, FailedCount: result.FailedCount,
		Progress: 100, ErrorCode: result.ErrorCode, ErrorSummary: result.ErrorSummary,
		StartedAt: result.StartedAt, CompletedAt: result.CompletedAt, UpdatedAt: result.UpdatedAt,
	}, nil
}

// ValidatePayload 确保统一队列客户端接收 struct 或 map，而非预序列化字节。
func ValidatePayload(payload any) error {
	if payload == nil {
		return stderrors.New("任务载荷不能为空")
	}
	kind := reflect.TypeOf(payload).Kind()
	if kind != reflect.Struct && kind != reflect.Map && kind != reflect.Ptr {
		return stderrors.New("任务载荷必须是结构体或 map")
	}
	if kind == reflect.Ptr && reflect.TypeOf(payload).Elem().Kind() != reflect.Struct {
		return stderrors.New("任务载荷指针必须指向结构体")
	}
	return nil
}
