package service

import (
	"fmt"
	"time"

	"metazone.cc/metalab/internal/common"
	"metazone.cc/metalab/internal/model"
)

// Energize 赋能文章
// 返回：获得的经验值、错误
func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int, error) {
	// 验证赋能量
	if amount != EnergyEnergize1 && amount != EnergyEnergize2 {
		return 0, common.ErrInvalidEnergyAmount
	}

	var expGained int
	var needExpUpdate bool
	var expToday time.Time
	var expTotal int

	err := s.repo.Transaction(func(txRepo EnergyStore, txFundRepo FundStore) error {
		// 1. 检查帖子存在，获取作者
		post, err := txRepo.FindPostByID(postID)
		if err != nil {
			return fmt.Errorf("查询帖子失败: %w", err)
		}

		// 2. 不能给自己赋能
		if post.UserID == energizerID {
			return common.ErrCannotEnergizeSelf
		}

		// 3. 检查赋能者域能余额（owner 跳过）
		energizer, err := txRepo.FindUserByID(energizerID)
		if err != nil {
			return fmt.Errorf("查询用户失败: %w", err)
		}
		if energizer.Role != model.RoleOwner && energizer.Energy < amount {
			return common.ErrInsufficientEnergy
		}

		// 4. 检查单用户单文章赋能总量 ≤ MaxEnergizePerPost
		total, err := txRepo.SumUserPostEnergy(energizerID, postID)
		if err != nil {
			return fmt.Errorf("查询赋能记录失败: %w", err)
		}
		if total+amount > MaxEnergizePerPost {
			return common.ErrEnergizeLimitReached
		}

		// 5. 扣赋能者域能
		if err := txRepo.AddEnergy(energizerID, -amount); err != nil {
			return fmt.Errorf("扣减域能失败: %w", err)
		}

		// 6. 增加被赋能者（文章作者）域能（赋能量的十分之一）
		receivedAmount := amount / 10 // 1 域能→0.1，2 域能→0.2
		if err := txRepo.AddEnergy(post.UserID, receivedAmount); err != nil {
			return fmt.Errorf("增加被赋能者域能失败: %w", err)
		}

		// 7. 更新 Post.total_energy_received
		if err := txRepo.IncrPostEnergy(postID, amount); err != nil {
			return fmt.Errorf("更新文章赋能计数失败: %w", err)
		}

		// 8. 插入赋能记录
		eLog := &model.PostEnergizeLog{
			UserID: energizerID,
			PostID: postID,
			Amount: amount,
		}
		if err := txRepo.CreateEnergizeLog(eLog); err != nil {
			return fmt.Errorf("创建赋能记录失败: %w", err)
		}

		// 9. 写入域能流水 ×2
		amountDisplay := float64(amount) / 10
		receivedDisplay := float64(receivedAmount) / 10

		energizerLog := &model.EnergyLog{
			UserID:      energizerID,
			Amount:      -amount,
			Type:        model.EnergyTypeEnergize,
			RelatedType: "post",
			RelatedID:   postID,
			Description: fmt.Sprintf("给文章 %d 赋能 %.1f 域能", postID, amountDisplay),
		}
		if err := txRepo.CreateEnergyLog(energizerLog); err != nil {
			return fmt.Errorf("创建赋能者流水失败: %w", err)
		}

		energizedLog := &model.EnergyLog{
			UserID:      post.UserID,
			Amount:      receivedAmount,
			Type:        model.EnergyTypeEnergized,
			RelatedType: "post",
			RelatedID:   postID,
			Description: fmt.Sprintf("文章 %d 被赋能 +%.1f 域能", postID, receivedDisplay),
		}
		if err := txRepo.CreateEnergyLog(energizedLog); err != nil {
			return fmt.Errorf("创建被赋能者流水失败: %w", err)
		}

		// 10. 赋能税收入公户（差价 = amount - receivedAmount）
		if txFundRepo != nil {
			tax := amount - receivedAmount
			if tax > 0 {
				if err := txFundRepo.IncrBalance(tax); err != nil {
					return fmt.Errorf("公户入账失败: %w", err)
				}
				taxDisplay := float64(tax) / 10
				fundLog := &model.FundLog{
					Amount:      tax,
					Type:        model.FundTypeEnergizeTax,
					RelatedType: "post",
					RelatedID:   postID,
					Description: fmt.Sprintf("赋能文章 #%d 税收 %.1f 域能，赋能者 uid=%d", postID, taxDisplay, energizerID),
				}
				if err := txFundRepo.CreateLog(fundLog); err != nil {
					return fmt.Errorf("创建公户流水失败: %w", err)
				}
			}
		}

		// 11. 计算经验值（溢出截断）—— 在事务内读取以保证一致性
		loc, _ := time.LoadLocation("Asia/Shanghai")
		now := time.Now().In(loc)
		today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)

		summary, err := txRepo.GetDailyEnergizeExp(energizerID, today)
		currentDaily := 0
		if err == nil && summary != nil {
			currentDaily = summary.ExpEarned
		}

		expGain := amount // 1域能(DB10) → 10经验, 2域能(DB20) → 20经验
		expRoom := DailyEnergizeExpCap - currentDaily
		if expRoom < 0 {
			expRoom = 0
		}
		if expGain > expRoom {
			expGain = expRoom
		}

		expGained = expGain
		if expGain > 0 {
			needExpUpdate = true
			expToday = today
			expTotal = currentDaily + expGain
		}
		return nil
	})
	if err != nil {
		return 0, err
	}

	// 12. 事务提交后，在事务外增加经验 + 更新每日汇总
	if needExpUpdate {
		if s.expSvc != nil {
			if _, _, err := s.expSvc.AddExp(energizerID, expGained); err != nil {
				return expGained, fmt.Errorf("赋能成功，但增加经验失败: %w", err)
			}
		}
		newSummary := &model.DailyExpSummary{
			UserID:    energizerID,
			Date:      expToday,
			ExpEarned: expTotal,
		}
		if err := s.repo.UpsertDailyExp(newSummary); err != nil {
			return expGained, fmt.Errorf("赋能成功，但更新经验汇总失败: %w", err)
		}
	}

	return expGained, nil
}
