package title

import (
	"context"
	"encoding/json"
	"time"

	"bindbox-game/internal/pkg/logger"
	"bindbox-game/internal/repository/mysql"
	"bindbox-game/internal/repository/mysql/dao"
	"bindbox-game/internal/repository/mysql/model"
)

// Service 提供头衔效果解析与领取型权益限流的服务
// Params:
// - ctx: 上下文
// - 依赖通过构造函数注入
// Returns:
// - 头衔相关功能的服务实例
type Service interface {
	// ResolveActiveEffects 解析用户在指定上下文(issue/activity/category)下的激活头衔效果
	// Params:
	// - ctx: 上下文
	// - userID: 用户ID
	// - scope: 事件作用域（可空字段用于精细过滤）
	// Returns:
	// - 解析后的效果列表（已按scopes过滤并仅保留启用效果）
	ResolveActiveEffects(ctx context.Context, userID int64, scope EffectScope) ([]*model.SystemTitleEffects, error)
	// AssignUserTitle 为用户分配或激活称号，并保证仅有一个激活称号
	// Params:
	// - ctx: 上下文
	// - userID: 用户ID
	// - titleID: 称号ID
	// - expiresAt: 可选过期时间
	// - remark: 备注
	// Returns:
	// - 错误信息（若已拥有且未过期则返回错误）
	AssignUserTitle(ctx context.Context, userID int64, titleID int64, expiresAt *time.Time, remark string) error
	ValidateEffectParams(effectType int32, raw string) (string, error)
}

type service struct {
	logger  logger.CustomLogger
	readDB  *dao.Query
	writeDB *dao.Query
}

// New 创建头衔服务实例
// Params:
// - l: 日志器
// - db: 数据库仓库
// Returns:
// - 头衔服务实例
func New(l logger.CustomLogger, db mysql.Repo) Service {
	return &service{
		logger:  l,
		readDB:  dao.Use(db.GetDbR()),
		writeDB: dao.Use(db.GetDbW()),
	}
}

// EffectScope 事件上下文作用域
// 用于按活动/期/分类过滤效果生效范围
type EffectScope struct {
	ActivityID       *int64
	IssueID          *int64
	ActivityCategory *int64
}

// scopesPayload 用于解析SystemTitleEffects.ScopesJSON
type scopesPayload struct {
	ActivityIDs []int64 `json:"activity_ids"`
	IssueIDs    []int64 `json:"issue_ids"`
	CategoryIDs []int64 `json:"category_ids"`
	Exclude     struct {
		ActivityIDs []int64 `json:"activity_ids"`
		IssueIDs    []int64 `json:"issue_ids"`
		CategoryIDs []int64 `json:"category_ids"`
	} `json:"exclude"`
}

// ResolveActiveEffects 查询用户激活头衔并解析效果，返回在给定scope内生效的效果
// Params:
// - ctx: 上下文
// - userID: 用户ID
// - scope: 事件作用域
// Returns:
// - 生效效果列表
func (s *service) ResolveActiveEffects(ctx context.Context, userID int64, scope EffectScope) ([]*model.SystemTitleEffects, error) {
	now := time.Now()
	titles, err := s.readDB.UserTitles.WithContext(ctx).
		Where(s.readDB.UserTitles.UserID.Eq(userID)).
		Where(s.readDB.UserTitles.Active.Eq(1)).
		Find()
	if err != nil {
		return nil, err
	}
	if len(titles) == 0 {
		return []*model.SystemTitleEffects{}, nil
	}
	var selID int64
	var selAt time.Time
	for _, ut := range titles {
		if ut.ExpiresAt.IsZero() || ut.ExpiresAt.After(now) {
			if selID == 0 || ut.ObtainedAt.After(selAt) {
				selID = ut.TitleID
				selAt = ut.ObtainedAt
			}
		}
	}
	if selID == 0 {
		return []*model.SystemTitleEffects{}, nil
	}
	effects, err := s.readDB.SystemTitleEffects.WithContext(ctx).
		Where(s.readDB.SystemTitleEffects.TitleID.Eq(selID)).
		Where(s.readDB.SystemTitleEffects.Status.Eq(1)).
		Order(s.readDB.SystemTitleEffects.Sort).
		Find()
	if err != nil {
		return nil, err
	}
	if len(effects) == 0 {
		return []*model.SystemTitleEffects{}, nil
	}
	var result []*model.SystemTitleEffects
	for _, ef := range effects {
		if ef.ScopesJSON == "" {
			result = append(result, ef)
			continue
		}
		var sc scopesPayload
		if err := json.Unmarshal([]byte(ef.ScopesJSON), &sc); err != nil {
			// 解析失败时按“全局生效”处理
			result = append(result, ef)
			continue
		}
		if !scopeMatch(scope, sc) {
			continue
		}
		result = append(result, ef)
	}
	return result, nil
}

// scopeMatch 判断效果scope是否命中当前事件上下文
// Params:
// - scope: 事件作用域
// - sc: 效果配置的scope载荷
// Returns:
// - 是否命中
func scopeMatch(scope EffectScope, sc scopesPayload) bool {
	// 排除优先
	if scope.ActivityID != nil && containsInt64(sc.Exclude.ActivityIDs, *scope.ActivityID) {
		return false
	}
	if scope.IssueID != nil && containsInt64(sc.Exclude.IssueIDs, *scope.IssueID) {
		return false
	}
	if scope.ActivityCategory != nil && containsInt64(sc.Exclude.CategoryIDs, *scope.ActivityCategory) {
		return false
	}
	// 包含判定（未配置即视为全局）
	if scope.ActivityID != nil && len(sc.ActivityIDs) > 0 && !containsInt64(sc.ActivityIDs, *scope.ActivityID) {
		return false
	}
	if scope.IssueID != nil && len(sc.IssueIDs) > 0 && !containsInt64(sc.IssueIDs, *scope.IssueID) {
		return false
	}
	if scope.ActivityCategory != nil && len(sc.CategoryIDs) > 0 && !containsInt64(sc.CategoryIDs, *scope.ActivityCategory) {
		return false
	}
	return true
}

// containsInt64 判断切片是否包含指定值
// Params:
// - arr: 切片
// - v: 值
// Returns:
// - 是否包含
func containsInt64(arr []int64, v int64) bool {
	for _, x := range arr {
		if x == v {
			return true
		}
	}
	return false
}
