package queue

import (
	"context"
	"fmt"

	"github.com/break/junhong_cmp_fiber/pkg/asynctask"
	"github.com/break/junhong_cmp_fiber/pkg/config"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/bytedance/sonic"
	"github.com/hibiken/asynq"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
)

// Client Asynq 任务提交客户端
type Client struct {
	client *asynq.Client
	logger *zap.Logger
}

// NewClient 创建新的 Asynq 客户端
func NewClient(redisClient *redis.Client, logger *zap.Logger) *Client {
	// 从 Redis 客户端获取配置
	opts := redisClient.Options()

	asynqClient := asynq.NewClient(asynq.RedisClientOpt{
		Addr:     opts.Addr,
		Password: opts.Password,
		DB:       opts.DB,
	})

	return &Client{
		client: asynqClient,
		logger: logger,
	}
}

// EnqueueTask 提交任务到队列
// payload 必须传入 struct 或 map，禁止传入 []byte。
// 传入 []byte 会导致 sonic.Marshal 将其 base64 编码，handler 解析时类型不匹配。
func (c *Client) EnqueueTask(ctx context.Context, taskType string, payload interface{}, opts ...asynq.Option) error {
	if err := asynctask.ValidatePayload(payload); err != nil {
		c.logger.Error("任务载荷类型错误，禁止传入 []byte",
			zap.String("task_type", taskType), zap.Error(err))
		return err
	}

	// 内部统一序列化，调用方无需预先 Marshal
	payloadBytes, err := sonic.Marshal(payload)
	if err != nil {
		c.logger.Error("任务载荷序列化失败",
			zap.String("task_type", taskType),
			zap.Error(err))
		return fmt.Errorf("failed to marshal task payload: %w", err)
	}

	opts = append(opts, asynq.Queue(constants.QueueForTaskType(taskType)))

	// 创建任务
	task := asynq.NewTask(taskType, payloadBytes, opts...)

	// 提交任务
	info, err := c.client.EnqueueContext(ctx, task)
	if err != nil {
		c.logger.Error("任务提交失败",
			zap.String("task_type", taskType),
			zap.Error(err))
		return fmt.Errorf("failed to enqueue task: %w", err)
	}

	c.logger.Info("任务已提交",
		zap.String("task_id", info.ID),
		zap.String("task_type", taskType),
		zap.String("queue", info.Queue),
		zap.Int("max_retry", info.MaxRetry))

	return nil
}

// Close 关闭客户端
func (c *Client) Close() error {
	if c.client != nil {
		return c.client.Close()
	}
	return nil
}

// ParseQueueConfig 解析队列配置为 Asynq 格式
func ParseQueueConfig(cfg *config.QueueConfig) map[string]int {
	queues := constants.DefaultTaskQueueWeights()
	if cfg.Queues != nil && len(cfg.Queues) > 0 {
		// 配置只覆盖权重；任务队列全集由代码兜底，避免漏监听新队列。
		for queueName, weight := range cfg.Queues {
			queues[queueName] = weight
		}
	}
	return queues
}
