// Package asset_package_batch_order 提供单列 CSV 资产套餐批量订购任务能力。
package asset_package_batch_order

import (
	"context"
	"path/filepath"
	"strings"
	"time"

	"github.com/hibiken/asynq"
	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/asynctask"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
	"github.com/break/junhong_cmp_fiber/pkg/queue"
)

// Service 资产套餐批量订购任务服务。
type Service struct {
	taskStore    *postgres.AssetPackageBatchOrderTaskStore
	packageStore *postgres.PackageStore
	queueClient  *queue.Client
}

// New 创建资产套餐批量订购任务服务。
func New(taskStore *postgres.AssetPackageBatchOrderTaskStore, packageStore *postgres.PackageStore, queueClient *queue.Client) *Service {
	return &Service{taskStore: taskStore, packageStore: packageStore, queueClient: queueClient}
}

// TaskPayload 批量订购 Worker 结构化载荷。
type TaskPayload struct {
	TaskID uint `json:"task_id"`
}

// Create 创建批量订购任务。
func (s *Service) Create(ctx context.Context, req *dto.CreateAssetPackageBatchOrderRequest) (*dto.AssetPackageBatchOrderTaskResponse, error) {
	if !strings.EqualFold(filepath.Ext(req.FileKey), ".csv") {
		return nil, errors.New(errors.CodeInvalidParam, "批量订购文件必须为CSV格式")
	}
	if !strings.HasPrefix(req.FileKey, constants.AssetPackageBatchOrderStoragePrefix+"/") {
		return nil, errors.New(errors.CodeInvalidParam, "批量订购文件Key不属于指定上传目录")
	}
	if err := validatePaymentParameters(req); err != nil {
		return nil, err
	}
	userID := middleware.GetUserIDFromContext(ctx)
	if userID == 0 {
		return nil, errors.New(errors.CodeUnauthorized)
	}
	pkg, err := s.packageStore.GetByID(ctx, req.PackageID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "套餐不存在")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询套餐失败")
	}
	task := &model.AssetPackageBatchOrderTask{
		TaskNo: s.taskStore.GenerateTaskNo(), PackageID: pkg.ID,
		PackageCode: pkg.PackageCode, PackageName: pkg.PackageName,
		PaymentMethod: req.PaymentMethod, FileName: filepath.Base(req.FileKey), StorageKey: req.FileKey,
		VoucherKeys: model.StringJSONBArray(req.VoucherKeys), Status: asynctask.StatusPending,
		ResultItems: model.AssetPackageBatchOrderResultItems{}, Creator: userID, Updater: userID,
		CreatorUserType: middleware.GetUserTypeFromContext(ctx), CreatorShopID: middleware.GetShopIDFromContext(ctx),
		CreatorName: middleware.GetUsernameFromContext(ctx),
	}
	if err := s.taskStore.Create(ctx, task); err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "创建批量订购任务失败")
	}
	var enqueueErr error
	if s.queueClient == nil {
		enqueueErr = errors.New(errors.CodeTaskQueueError, "批量订购任务队列未配置")
	} else {
		enqueueErr = s.queueClient.EnqueueTask(ctx, constants.TaskTypeAssetPackageBatchOrder, TaskPayload{TaskID: task.ID},
			asynq.Queue(constants.QueueForTaskType(constants.TaskTypeAssetPackageBatchOrder)),
			asynq.Timeout(constants.AssetPackageBatchOrderTaskTimeout))
	}
	if enqueueErr != nil {
		message := "批量订购任务入队失败"
		_ = s.taskStore.MarkFailed(ctx, task.ID, message)
		task.Status, task.ErrorMessage = asynctask.StatusFailed, message
		now := time.Now()
		task.CompletedAt = &now
	}
	return toResponse(task), nil
}

// List 分页查询批量订购任务。
func (s *Service) List(ctx context.Context, req *dto.ListAssetPackageBatchOrderRequest) (*dto.AssetPackageBatchOrderTaskListResponse, error) {
	if req.Page <= 0 {
		req.Page = constants.DefaultPage
	}
	if req.PageSize <= 0 {
		req.PageSize = constants.DefaultPageSize
	}
	tasks, total, err := s.taskStore.List(ctx, &store.QueryOptions{Page: req.Page, PageSize: req.PageSize}, req.Status)
	if err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询批量订购任务失败")
	}
	items := make([]*dto.AssetPackageBatchOrderTaskResponse, 0, len(tasks))
	for _, task := range tasks {
		items = append(items, toResponse(task))
	}
	return &dto.AssetPackageBatchOrderTaskListResponse{Total: total, Page: req.Page, PageSize: req.PageSize, Items: items}, nil
}

// GetByID 查询批量订购任务及逐行结果。
func (s *Service) GetByID(ctx context.Context, id uint) (*dto.AssetPackageBatchOrderTaskDetailResponse, error) {
	task, err := s.taskStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "批量订购任务不存在")
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询批量订购任务失败")
	}
	items := make([]dto.AssetPackageBatchOrderResultItemResponse, 0, len(task.ResultItems))
	for _, item := range task.ResultItems {
		items = append(items, dto.AssetPackageBatchOrderResultItemResponse{
			Line: item.Line, AssetIdentifier: item.AssetIdentifier, Status: item.Status,
			StatusName: constants.GetAssetPackageBatchOrderItemStatusName(item.Status),
			OrderID:    item.OrderID, OrderNo: item.OrderNo, Amount: item.Amount, Reason: item.Reason,
		})
	}
	return &dto.AssetPackageBatchOrderTaskDetailResponse{AssetPackageBatchOrderTaskResponse: *toResponse(task), Items: items}, nil
}

func validatePaymentParameters(req *dto.CreateAssetPackageBatchOrderRequest) error {
	if req.PaymentMethod == model.PaymentMethodWallet && len(req.VoucherKeys) > 0 {
		return errors.New(errors.CodeInvalidParam, "钱包支付不能提交线下凭证")
	}
	if req.PaymentMethod == model.PaymentMethodOffline && len(req.VoucherKeys) == 0 {
		return errors.New(errors.CodeInvalidParam, "线下支付必须提交至少一个凭证")
	}
	return nil
}

func toResponse(task *model.AssetPackageBatchOrderTask) *dto.AssetPackageBatchOrderTaskResponse {
	response := &dto.AssetPackageBatchOrderTaskResponse{
		ID: task.ID, TaskNo: task.TaskNo, PackageID: task.PackageID, PackageCode: task.PackageCode,
		PackageName: task.PackageName, PaymentMethod: task.PaymentMethod, FileName: task.FileName,
		VoucherKeys: []string(task.VoucherKeys), Status: task.Status, StatusName: asynctask.StatusName(task.Status),
		TotalCount: task.TotalCount, SuccessCount: task.SuccessCount, FailCount: task.FailCount,
		ErrorMessage: task.ErrorMessage, CreatorName: task.CreatorName,
		CreatedAt: task.CreatedAt.Format(time.RFC3339), UpdatedAt: task.UpdatedAt.Format(time.RFC3339),
	}
	if response.VoucherKeys == nil {
		response.VoucherKeys = []string{}
	}
	if task.StartedAt != nil {
		value := task.StartedAt.Format(time.RFC3339)
		response.StartedAt = &value
	}
	if task.CompletedAt != nil {
		value := task.CompletedAt.Format(time.RFC3339)
		response.CompletedAt = &value
	}
	return response
}
