package service

import (
	"fmt"
	"time"

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

	"gorm.io/gorm"
)

// expAdder EnergyService 对经验增加的最小依赖（ISP）
type expAdder interface {
	AddExp(userID uint, delta int) (newExp int, levelUp bool, err error)
}

// EnergyService 域能业务逻辑
type EnergyService struct {
	db       *gorm.DB
	repo     EnergyStore
	expSvc   expAdder
	notifSvc energyNotifier
}

// NewEnergyService 构造函数
func NewEnergyService(db *gorm.DB, repo EnergyStore, expSvc expAdder, notifSvc energyNotifier) *EnergyService {
	return &EnergyService{db: db, repo: repo, expSvc: expSvc, notifSvc: notifSvc}
}

// EnergyInfo 域能信息（含每日赋能经验状态）
type EnergyInfo struct {
	Energy             int  // 域能余额（乘10存储）
	DailyEnergizeExp   int  // 当日已通过赋能获得的经验值
	DailyExpCap        int  // 每日赋能经验上限
	DailyExpCapReached bool // 是否已达上限
}

// SetExpAdder 链式注入经验服务（用于赋能获得经验）
func (s *EnergyService) SetExpAdder(svc expAdder) {
	s.expSvc = svc
}

// 域能相关常量（乘10存储，DB值）
const (
	EnergyCheckIn    = 10   // +1.0 域能
	EnergyRename     = -60  // -6.0 域能
	EnergyRenameRefund = 60 // +6.0 域能
	EnergyDeletePost = -20  // -2.0 域能
	EnergyEnergize1  = 10   // 轻赋 1 域能
	EnergyEnergize2  = 20   // 重赋 2 域能
	EnergizeExpPerEnergy = 10 // 每 1 域能 = 10 经验
	DailyEnergizeExpCap  = 50 // 每日赋能经验上限
	MaxEnergizePerPost   = 20 // 单用户单文章最多赋能 2 域能（×10）
)

// 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

	err := s.db.Transaction(func(tx *gorm.DB) error {
		txRepo := s.repo.WithTx(tx)

		// 1. 检查帖子存在，获取作者
		post, err := txRepo.FindPostByID(postID)
		if err != nil {
			if err == gorm.ErrRecordNotFound {
				return common.ErrPostNotFound
			}
			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. 计算经验值（溢出截断）
		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
		}

		// 11. 增加经验 + 更新每日经验汇总
		if expGain > 0 {
			if s.expSvc != nil {
				_, _, err = s.expSvc.AddExp(energizerID, expGain)
				if err != nil {
					return fmt.Errorf("增加经验失败: %w", err)
				}
			}

			newSummary := &model.DailyExpSummary{
				UserID:    energizerID,
				Date:      today,
				ExpEarned: currentDaily + expGain,
			}
			if err := txRepo.UpsertDailyExp(newSummary); err != nil {
				return fmt.Errorf("更新每日经验汇总失败: %w", err)
			}
		}

		expGained = expGain
		return nil
	})

	return expGained, err
}

// CheckInEnergy 签到获得域能（LV0 用户不调用此方法）
func (s *EnergyService) CheckInEnergy(userID uint) error {
	if err := s.repo.AddEnergy(userID, EnergyCheckIn); err != nil {
		return fmt.Errorf("增加签到域能失败: %w", err)
	}

	log := &model.EnergyLog{
		UserID:      userID,
		Amount:      EnergyCheckIn,
		Type:        model.EnergyTypeSignIn,
		Description: "每日签到 +1.0 域能",
	}
	return s.repo.CreateEnergyLog(log)
}

// DeductOnRename 改名扣域能（首次改名免费，不调用此方法）
func (s *EnergyService) DeductOnRename(userID uint) error {
	// 检查域能余额（非 owner 需 ≥ 0）
	user, err := s.repo.FindUserByID(userID)
	if err != nil {
		return fmt.Errorf("查询用户失败: %w", err)
	}
	if user.Role != model.RoleOwner && user.Energy+EnergyRename < 0 {
		return common.ErrInsufficientEnergy
	}

	if err := s.repo.AddEnergy(userID, EnergyRename); err != nil {
		return fmt.Errorf("扣减改名域能失败: %w", err)
	}

	log := &model.EnergyLog{
		UserID:      userID,
		Amount:      EnergyRename,
		Type:        model.EnergyTypeRename,
		Description: "改名消耗 6.0 域能",
	}
	return s.repo.CreateEnergyLog(log)
}

// RefundOnRenameReject 改名审核被拒，返还域能
func (s *EnergyService) RefundOnRenameReject(userID uint) error {
	if err := s.repo.AddEnergy(userID, EnergyRenameRefund); err != nil {
		return fmt.Errorf("返还改名域能失败: %w", err)
	}

	log := &model.EnergyLog{
		UserID:      userID,
		Amount:      EnergyRenameRefund,
		Type:        model.EnergyTypeRenameRefund,
		Description: "改名审核被拒，返还 6.0 域能",
	}
	return s.repo.CreateEnergyLog(log)
}

// DeductOnDeletePost 删稿扣域能（扣文章作者的域能，无视负数）
func (s *EnergyService) DeductOnDeletePost(authorUserID uint, postID uint) error {
	if err := s.repo.AddEnergy(authorUserID, EnergyDeletePost); err != nil {
		return fmt.Errorf("扣减删稿域能失败: %w", err)
	}

	log := &model.EnergyLog{
		UserID:      authorUserID,
		Amount:      EnergyDeletePost,
		Type:        model.EnergyTypeDeletePost,
		RelatedType: "post",
		RelatedID:   postID,
		Description: "删除文章消耗 2.0 域能",
	}
	return s.repo.CreateEnergyLog(log)
}

// AdminAdjust 后台调整用户域能
func (s *EnergyService) AdminAdjust(operatorUID uint, userIDs []uint, amount int, description string) error {
	for _, uid := range userIDs {
		if err := s.repo.AddEnergy(uid, amount); err != nil {
			return fmt.Errorf("调整用户 %d 域能失败: %w", uid, err)
		}

		opUID := operatorUID
		log := &model.EnergyLog{
			UserID:      uid,
			Amount:      amount,
			Type:        model.EnergyTypeAdminAdjust,
			Description: description,
			OperatorUID: &opUID,
		}
		if err := s.repo.CreateEnergyLog(log); err != nil {
			return fmt.Errorf("创建调整流水失败: %w", err)
		}
	}
	return nil
}

// GetEnergyInfo 获取用户域能余额和每日赋能经验状态
func (s *EnergyService) GetEnergyInfo(userID uint) (*EnergyInfo, error) {
	user, err := s.repo.FindUserByID(userID)
	if err != nil {
		return nil, err
	}

	loc, _ := time.LoadLocation("Asia/Shanghai")
	now := time.Now().In(loc)
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)

	dailyExp := 0
	summary, err := s.repo.GetDailyEnergizeExp(userID, today)
	if err == nil && summary != nil {
		dailyExp = summary.ExpEarned
	}

	return &EnergyInfo{
		Energy:             user.Energy,
		DailyEnergizeExp:   dailyExp,
		DailyExpCap:        DailyEnergizeExpCap,
		DailyExpCapReached: dailyExp >= DailyEnergizeExpCap,
	}, nil
}

// GetEnergyLogs 分页查询用户域能流水（近 N 天）
func (s *EnergyService) GetEnergyLogs(userID uint, days, page, pageSize int) ([]model.EnergyLog, int64, error) {
	p := common.Pagination{Page: page, PageSize: pageSize}
	p.DefaultPagination()

	total, err := s.repo.CountEnergyLogsByUser(userID, days)
	if err != nil {
		return nil, 0, err
	}

	logs, err := s.repo.ListEnergyLogsByUser(userID, days, p.Offset(), p.PageSize)
	if err != nil {
		return nil, 0, err
	}
	if logs == nil {
		logs = []model.EnergyLog{}
	}
	return logs, total, nil
}

// GetAdminEnergyLogs 后台查询全部域能流水
func (s *EnergyService) GetAdminEnergyLogs(energyType string, page, pageSize int) ([]model.EnergyLog, int64, error) {
	p := common.Pagination{Page: page, PageSize: pageSize}
	p.DefaultPagination()
	return s.repo.ListAllEnergyLogs(energyType, p.Offset(), p.PageSize)
}
