package packagepkg

import (
	"context"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// ResumeCallback 复机回调接口
// 用于在套餐激活或流量重置后触发自动复机
type ResumeCallback interface {
	ResumeCardIfStopped(ctx context.Context, carrierType string, carrierID uint) error
}

type ActivationService struct {
	db                      *gorm.DB
	redis                   *redis.Client
	packageUsageStore       *postgres.PackageUsageStore
	packageStore            *postgres.PackageStore
	packageUsageDailyRecord *postgres.PackageUsageDailyRecordStore
	logger                  *zap.Logger
	resumeCallback          ResumeCallback // 复机回调，可选
}

func NewActivationService(
	db *gorm.DB,
	redis *redis.Client,
	packageUsageStore *postgres.PackageUsageStore,
	packageStore *postgres.PackageStore,
	packageUsageDailyRecord *postgres.PackageUsageDailyRecordStore,
	logger *zap.Logger,
) *ActivationService {
	return &ActivationService{
		db:                      db,
		redis:                   redis,
		packageUsageStore:       packageUsageStore,
		packageStore:            packageStore,
		packageUsageDailyRecord: packageUsageDailyRecord,
		logger:                  logger,
	}
}

// SetResumeCallback 任务 24.7: 设置复机回调
// 在应用启动时由 bootstrap 调用，注入停复机服务
func (s *ActivationService) SetResumeCallback(callback ResumeCallback) {
	s.resumeCallback = callback
}

// ActivateByRealname 任务 9.2-9.3: 首次实名激活
// 当用户完成实名后，激活所有待实名激活的套餐
func (s *ActivationService) ActivateByRealname(ctx context.Context, carrierType string, carrierID uint) error {
	// 查询待实名激活的套餐
	var pendingUsages []*model.PackageUsage
	query := s.db.WithContext(ctx).
		Where("pending_realname_activation = ?", true).
		Where("status = ?", constants.PackageUsageStatusPending)

	if carrierType == "iot_card" {
		query = query.Where("iot_card_id = ?", carrierID)
	} else if carrierType == "device" {
		query = query.Where("device_id = ?", carrierID)
	} else {
		return errors.New(errors.CodeInvalidParam, "无效的载体类型")
	}

	if err := query.Order("priority ASC").Find(&pendingUsages).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "查询待实名激活套餐失败")
	}

	if len(pendingUsages) == 0 {
		s.logger.Info("没有待实名激活的套餐", zap.String("carrier_type", carrierType), zap.Uint("carrier_id", carrierID))
		return nil
	}

	now := time.Now()

	// 在事务中激活套餐
	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		for _, usage := range pendingUsages {
			// 查询套餐信息
			var pkg model.Package
			if err := tx.First(&pkg, usage.PackageID).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "查询套餐信息失败")
			}

			// 检查是否是主套餐
			if usage.MasterUsageID == nil {
				// 主套餐：需要检查是否有已激活的主套餐
				var activeMain model.PackageUsage
				err := tx.Where("status = ?", constants.PackageUsageStatusActive).
					Where("master_usage_id IS NULL").
					Where(carrierType+"_id = ?", carrierID).
					Order("priority ASC").
					First(&activeMain).Error

				if err == nil {
					// 已有激活的主套餐，保持排队状态
					s.logger.Warn("已有激活主套餐，跳过激活",
						zap.Uint("usage_id", usage.ID),
						zap.Uint("active_main_id", activeMain.ID))
					continue
				}
				if err != gorm.ErrRecordNotFound {
					return errors.Wrap(errors.CodeDatabaseError, err, "检查生效中主套餐失败")
				}
			}

			// REALNAME-04: 按 ExpiryBase 选择激活计时基准
			// from_purchase：购买时起算，用 PackageUsage 创建时间；否则用实名触发当前时刻
			var activatedAt time.Time
			if pkg.ExpiryBase == "from_purchase" {
				activatedAt = usage.CreatedAt
			} else {
				activatedAt = now
			}
			expiresAt := CalculateExpiryTime(pkg.CalendarType, activatedAt, pkg.DurationMonths, pkg.DurationDays)

			// 计算下次重置时间
			nextResetAt := CalculateNextResetTime(pkg.DataResetCycle, pkg.CalendarType, now, activatedAt)

			// 更新套餐使用记录
			updates := map[string]interface{}{
				"status":                      constants.PackageUsageStatusActive,
				"pending_realname_activation": false,
				"activated_at":                activatedAt,
				"expires_at":                  expiresAt,
			}
			if nextResetAt != nil {
				updates["next_reset_at"] = *nextResetAt
			}

			if err := tx.Model(usage).Updates(updates).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "激活套餐失败")
			}

			s.syncCarrierStatusActivated(ctx, tx, usage, carrierType, carrierID)

			s.logger.Info("套餐已激活",
				zap.Uint("usage_id", usage.ID),
				zap.Uint("package_id", usage.PackageID),
				zap.Time("activated_at", activatedAt),
				zap.Time("expires_at", expiresAt))

			if s.resumeCallback != nil {
				go func(ct string, cid uint) {
					if err := s.resumeCallback.ResumeCardIfStopped(context.Background(), ct, cid); err != nil {
						s.logger.Error("实名激活后自动复机失败",
							zap.String("carrier_type", ct),
							zap.Uint("carrier_id", cid),
							zap.Error(err))
					}
				}(carrierType, carrierID)
			}
		}

		return nil
	})
}

// ActivateQueuedPackage 任务 9.4-9.7: 排队主套餐激活
// 当主套餐过期后，激活下一个待生效的主套餐
func (s *ActivationService) ActivateQueuedPackage(ctx context.Context, carrierType string, carrierID uint) error {
	// 使用 Redis 分布式锁避免并发
	lockKey := constants.RedisPackageActivationLockKey(carrierType, carrierID)
	lockValue := time.Now().String()
	locked, err := s.redis.SetNX(ctx, lockKey, lockValue, 30*time.Second).Result()
	if err != nil {
		return errors.Wrap(errors.CodeRedisError, err, "获取分布式锁失败")
	}
	if !locked {
		s.logger.Warn("套餐激活正在进行中，跳过",
			zap.String("carrier_type", carrierType),
			zap.Uint("carrier_id", carrierID))
		return nil
	}
	defer s.redis.Del(ctx, lockKey)

	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 任务 9.5: 检测并标记过期的主套餐
		now := time.Now()
		var expiredMainUsages []*model.PackageUsage
		err := tx.Where("status = ?", constants.PackageUsageStatusActive).
			Where("master_usage_id IS NULL").
			Where("expires_at <= ?", now).
			Where(carrierType+"_id = ?", carrierID).
			Find(&expiredMainUsages).Error

		if err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "查询过期主套餐失败")
		}

		for _, expiredMain := range expiredMainUsages {
			// 更新主套餐状态为已过期
			if err := tx.Model(expiredMain).Update("status", constants.PackageUsageStatusExpired).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "更新过期主套餐状态失败")
			}

			s.logger.Info("主套餐已过期",
				zap.Uint("usage_id", expiredMain.ID),
				zap.Time("expires_at", expiredMain.ExpiresAt))

			// 任务 9.7: 加油包级联失效
			if err := s.invalidateAddons(ctx, tx, expiredMain.ID); err != nil {
				return err
			}

			// 任务 9.6: 激活下一个待生效主套餐
			if err := s.activateNextMainPackage(ctx, tx, carrierType, carrierID, now); err != nil {
				return err
			}
		}

		return nil
	})
}

// invalidateAddons 任务 9.7: 加油包级联失效
func (s *ActivationService) invalidateAddons(ctx context.Context, tx *gorm.DB, masterUsageID uint) error {
	var addons []*model.PackageUsage
	if err := tx.Where("master_usage_id = ?", masterUsageID).
		Where("status IN ?", []int{constants.PackageUsageStatusActive, constants.PackageUsageStatusPending}).
		Find(&addons).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "查询加油包失败")
	}

	if len(addons) == 0 {
		return nil
	}

	addonIDs := make([]uint, len(addons))
	for i, addon := range addons {
		addonIDs[i] = addon.ID
	}

	// 批量更新加油包状态为已失效
	if err := tx.Model(&model.PackageUsage{}).
		Where("id IN ?", addonIDs).
		Update("status", constants.PackageUsageStatusInvalidated).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "批量失效加油包失败")
	}

	s.logger.Info("加油包已级联失效",
		zap.Uint("master_usage_id", masterUsageID),
		zap.Int("addon_count", len(addons)))

	return nil
}

// activateNextMainPackage 任务 9.6: 激活下一个待生效主套餐
func (s *ActivationService) activateNextMainPackage(ctx context.Context, tx *gorm.DB, carrierType string, carrierID uint, now time.Time) error {
	// 查询下一个待生效主套餐
	var nextMain model.PackageUsage
	err := tx.Where("status = ?", constants.PackageUsageStatusPending).
		Where("master_usage_id IS NULL").
		Where(carrierType+"_id = ?", carrierID).
		Order("priority ASC").
		First(&nextMain).Error

	if err == gorm.ErrRecordNotFound {
		s.logger.Info("没有待生效的主套餐",
			zap.String("carrier_type", carrierType),
			zap.Uint("carrier_id", carrierID))
		return nil
	}
	if err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "查询下一个待生效主套餐失败")
	}

	// 查询套餐信息
	var pkg model.Package
	if err := tx.First(&pkg, nextMain.PackageID).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "查询套餐信息失败")
	}

	// 激活套餐
	activatedAt := now
	expiresAt := CalculateExpiryTime(pkg.CalendarType, activatedAt, pkg.DurationMonths, pkg.DurationDays)

	// 计算下次重置时间
	nextResetAt := CalculateNextResetTime(pkg.DataResetCycle, pkg.CalendarType, now, activatedAt)

	// 更新套餐使用记录
	updates := map[string]interface{}{
		"status":       constants.PackageUsageStatusActive,
		"activated_at": activatedAt,
		"expires_at":   expiresAt,
	}
	if nextResetAt != nil {
		updates["next_reset_at"] = *nextResetAt
	}

	if err := tx.Model(&nextMain).Updates(updates).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "激活排队主套餐失败")
	}

	s.syncCarrierStatusActivated(ctx, tx, &nextMain, carrierType, carrierID)

	s.logger.Info("排队主套餐已激活",
		zap.Uint("usage_id", nextMain.ID),
		zap.Uint("package_id", nextMain.PackageID),
		zap.Time("activated_at", activatedAt),
		zap.Time("expires_at", expiresAt))

	if s.resumeCallback != nil {
		go func(ct string, cid uint) {
			if err := s.resumeCallback.ResumeCardIfStopped(context.Background(), ct, cid); err != nil {
				s.logger.Error("排队激活后自动复机失败",
					zap.String("carrier_type", ct),
					zap.Uint("carrier_id", cid),
					zap.Error(err))
			}
		}(carrierType, carrierID)
	}

	return nil
}

func (s *ActivationService) syncCarrierStatusActivated(ctx context.Context, tx *gorm.DB, usage *model.PackageUsage, carrierType string, carrierID uint) {
	if usage == nil {
		return
	}

	if usage.UsageType == constants.PackageUsageTypeDevice || usage.DeviceID > 0 || carrierType == constants.AssetTypeDevice {
		deviceID := usage.DeviceID
		if deviceID == 0 {
			deviceID = carrierID
		}
		if deviceID == 0 {
			return
		}

		if err := tx.WithContext(ctx).
			Model(&model.Device{}).
			Where("id = ? AND status < ?", deviceID, constants.DeviceStatusActivated).
			Update("status", constants.DeviceStatusActivated).Error; err != nil {
			s.logger.Warn("套餐激活后更新设备状态失败",
				zap.Uint("device_id", deviceID),
				zap.Uint("usage_id", usage.ID),
				zap.Error(err))
		}
		return
	}

	cardID := usage.IotCardID
	if cardID == 0 {
		cardID = carrierID
	}
	if cardID == 0 {
		return
	}

	if err := tx.WithContext(ctx).
		Model(&model.IotCard{}).
		Where("id = ? AND status < ?", cardID, constants.IotCardStatusActivated).
		Update("status", constants.IotCardStatusActivated).Error; err != nil {
		s.logger.Warn("套餐激活后更新卡状态失败",
			zap.Uint("iot_card_id", cardID),
			zap.Uint("usage_id", usage.ID),
			zap.Error(err))
	}
}

// InvalidateAllPackagesByAsset 批量失效资产关联的所有有效套餐
// 退款时调用：将该资产下状态为待生效(0)、生效中(1)、已用完(2)的套餐全部标记为已失效(4)
func (s *ActivationService) InvalidateAllPackagesByAsset(ctx context.Context, assetType string, assetID uint) error {
	query := s.db.WithContext(ctx).
		Model(&model.PackageUsage{}).
		Where("status IN ?", []int{
			constants.PackageUsageStatusPending,
			constants.PackageUsageStatusActive,
			constants.PackageUsageStatusDepleted,
		})

	switch assetType {
	case "iot_card":
		query = query.Where("iot_card_id = ?", assetID)
	case "device":
		query = query.Where("device_id = ?", assetID)
	default:
		return errors.New(errors.CodeInvalidParam, "无效的资产类型")
	}

	result := query.Update("status", constants.PackageUsageStatusInvalidated)
	if result.Error != nil {
		return errors.Wrap(errors.CodeDatabaseError, result.Error, "批量失效套餐失败")
	}

	s.logger.Info("批量失效套餐完成",
		zap.String("asset_type", assetType),
		zap.Uint("asset_id", assetID),
		zap.Int64("affected", result.RowsAffected),
	)

	return nil
}
