package appservice

import (
	"context"
	"errors"
	"fmt"
	"go-admin/app/admin/models"
	adminservice "go-admin/app/admin/service"
	"go-admin/app/admin/service/dto"
	memberbalancechangesource "go-admin/common/const/dicts/member_balance_change_source"
	memberrenwalconfigstatus "go-admin/common/const/dicts/member_renwal_config_status"
	memberrenwallogstatus "go-admin/common/const/dicts/member_renwal_log_status"
	"go-admin/common/const/rediskey"
	"go-admin/common/helper"
	statuscode "go-admin/common/status_code"
	"go-admin/pkg/utility"
	"time"

	"github.com/go-admin-team/go-admin-core/logger"
	"github.com/go-admin-team/go-admin-core/sdk/service"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
)

type LineUser struct {
	service.Service
}

// 注册赠送默认套餐
func (e *LineUser) SetDefaultRenwal(user *models.LineUser) error {
	var defaultRenwalConfig models.MemberRenwalConfig

	if err := e.Orm.Model(&defaultRenwalConfig).Where("is_default =1 AND status =?", memberrenwalconfigstatus.ENABLE).First(&defaultRenwalConfig).Error; err != nil {

		logger.Error("没有默认续费配置 err", err)
		return nil
	}

	now := time.Now()
	//续费时长
	if defaultRenwalConfig.DurationDay > 0 {
		renwalLog := models.MemberRenwaLog{
			RenwalId:            defaultRenwalConfig.Id,
			RenwalName:          defaultRenwalConfig.PackageName,
			UserId:              user.Id,
			RenwalDuration:      defaultRenwalConfig.DurationDay,
			Status:              memberrenwallogstatus.PENDING,
			PayableAmount:       defaultRenwalConfig.OriginalPrice,
			ActualPaymentAmount: decimal.Zero,
			PaymentTime:         &now,
		}

		err := e.Orm.Transaction(func(tx *gorm.DB) error {
			expirationTime := time.Now().Add(time.Hour * 24 * time.Duration(defaultRenwalConfig.DurationDay))
			user.ExpirationTime = &expirationTime

			if err := tx.Model(&user).Update("expiration_time", expirationTime).Error; err != nil {
				logger.Errorf("默认套餐修改过期时间失败 userid:%v err:%v", user.Id, err)
				return err
			}

			if err := tx.Create(&renwalLog).Error; err != nil {
				logger.Errorf("新增续费记录失败 userid:%v err:%v", user.Id, err)
				return err
			}

			return nil
		})

		if err != nil {
			logger.Error("续费失败 err", err)
		}
	}

	return nil
}

// 处理过期账户
func (e *LineUser) Expire() error {
	err := e.Orm.Model(&models.LineUser{}).Where("expiration_time <NOW() AND expiration_time IS NOT NULL ").Update("expiration_time", nil).Error

	return err
}

// 会员开通支付回调
func (e *LineUser) PayCallBack(datas []models.MemberRenwaLog) error {
	now := time.Now()
	// startTime := time.Now().Add(-4 * time.Hour)
	log := models.MemberRenwaLog{}
	configService := adminservice.SysConfig{Service: e.Service}
	keys := []string{"member_invitation_rate1", "member_invitation_rate2"}
	configs := make(map[string]dto.GetSysConfigByKEYForServiceResp)
	configService.GetWithKeys(&keys, &configs)
	renwalLogs := make([]models.MemberRenwaLog, 0)

	if err := e.Orm.Model(&models.MemberRenwaLog{}).Where("expiration_time >NOW() and status =?", memberrenwallogstatus.PENDING).Find(&renwalLogs).Error; err != nil {
		logger.Error("查询未过期续费记录失败", err)
		return err
	}

	for _, data := range datas {
		err := e.doCallBack(data, log, renwalLogs, now, configs)
		if err != nil {
			continue
		}
	}

	return nil
}

// 回调逻辑
func (e *LineUser) doCallBack(data models.MemberRenwaLog, log models.MemberRenwaLog, renwalLogs []models.MemberRenwaLog, now time.Time, configs map[string]dto.GetSysConfigByKEYForServiceResp) error {
	lock := helper.NewRedisLock(fmt.Sprintf(rediskey.OrderCallBackLock, data.PayableAmount), 200, 5, 100*time.Millisecond)

	if ok, err := lock.AcquireWait(context.Background()); err != nil {
		logger.Debug("获取锁失败", err)
		return err
	} else if ok {
		defer lock.Release()

		// 检查是否已经处理过该订单
		hashKey := fmt.Sprintf(rediskey.MemberHash, data.Hash)
		val, _ := helper.DefaultRedis.GetString(hashKey)
		if val != "" {
			logger.Info("已处理过的hash:", data.Hash)
			return nil
		}

		for index := range renwalLogs {
			if renwalLogs[index].PayableAmount.Cmp(data.PayableAmount) == 0 {
				// 查询续费记录
				if err := e.Orm.Model(&log).Where("payable_amount=? AND status =?", data.PayableAmount, memberrenwallogstatus.PENDING).First(&log).Error; err != nil {
					if errors.Is(err, gorm.ErrRecordNotFound) {

						// 如果记录未找到，跳过该项
						logger.Warnf("续费记录未找到 hash:%v amount:%v", data.Hash, data.PayableAmount)
						continue
					}
					logger.Errorf("续费记录查询失败 hash:%v amount:%v err:%v", data.Hash, data.PayableAmount, err)
					continue
				}

				log.Status = memberrenwallogstatus.PAID
				log.PaymentTime = &now
				log.FromAddress = data.FromAddress
				log.ActualPaymentAmount = data.ActualPaymentAmount
				log.Hash = data.Hash

				user := models.LineUser{}
				// 查询用户信息
				if err := e.Orm.Model(&user).Where("id =?", log.UserId).First(&user).Error; err != nil {
					logger.Errorf("用户查询失败 hash:%v amount:%v err:%v", data.Hash, data.PayableAmount, err)
					continue
				}
				// 更新 ExpirationTime
				if user.ExpirationTime != nil && user.ExpirationTime.After(now) {
					expirationTime := user.ExpirationTime.AddDate(0, 0, log.RenwalDuration)
					user.ExpirationTime = &expirationTime
				} else {
					expirationTime := now.AddDate(0, 0, log.RenwalDuration)
					user.ExpirationTime = &expirationTime
				}
				// 计算返现记录
				balanceLogs := make([]models.MemberBalanceLog, 0)
				if user.Pid > 0 {
					if set, ok := configs["member_invitation_rate1"]; ok {
						rate1 := utility.StrToDecimal(set.ConfigValue)
						if rate1.Cmp(decimal.Zero) > 0 {
							balanceLogs = append(balanceLogs, calculateBalance(user.Pid, data.PayableAmount, rate1))
						}
					}
				}

				if user.TopReferrerId > 0 {
					if set, ok := configs["member_invitation_rate2"]; ok {
						rate1 := utility.StrToDecimal(set.ConfigValue)
						if rate1.Cmp(decimal.Zero) > 0 {
							balanceLogs = append(balanceLogs, calculateBalance(user.TopReferrerId, data.PayableAmount, rate1))
						}
					}
				}
				// 修改续期订单记录
				if err := e.Orm.Model(log).Save(&log).Error; err != nil {
					logger.Errorf("续费更新订单失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err)
					continue
				}
				// 更新用户信息
				if err := e.Orm.Model(&user).Updates(user).Error; err != nil {
					logger.Errorf("续费更新失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err)
					continue
				}
				// 事务操作，保存返现记录及更新余额
				err := e.Orm.Transaction(func(tx *gorm.DB) error {
					// 保存返现记录
					if err := e.Orm.Model(&models.MemberBalanceLog{}).Create(&balanceLogs).Error; err != nil {
						logger.Errorf("续费保存返现记录失败 userid:%v hash:%v amount:%v err:%v", user.Id, data.Hash, data.PayableAmount, err)
						return err
					}

					// 更新用户余额
					for _, item := range balanceLogs {
						if err := e.Orm.Exec("update member_balance set total_amount=total_amount+?,free_amount=free_amount +? where user_id=?", item.Amount, item.Amount, item.UserId).Error; err != nil {
							logger.Errorf("续费更新余额失败 userid:%v 返现userid:%v hash:%v amount:%v err:%v", user.Id, item.UserId, data.Hash, data.PayableAmount, err)
							return err
						}
					}

					return nil
				})

				// 事务失败处理
				if err != nil {
					logger.Errorf("续费成功，返现失败:续费userid:%v,hash:%s,err:%v", user.Id, data.Hash, err)
					continue
				}

				if err := helper.DefaultRedis.SetStringExpire(hashKey, "1", time.Hour*1); err != nil {
					logger.Errorf("续费成功，写入hash缓存失败 :续费userid:%v,hash:%s,err:%v", user.Id, data.Hash, err)
				}
			}
		}
	}
	return nil
}

// 计算返现的函数
func calculateBalance(userId int, amount decimal.Decimal, rate decimal.Decimal) models.MemberBalanceLog {
	return models.MemberBalanceLog{
		UserId:       userId,
		ChangeSource: memberbalancechangesource.CASH_BACK,
		ChangeType:   1,
		Amount:       amount.Mul(rate.Div(decimal.NewFromInt(100))).Truncate(2),
	}
}

// 获取用户过期时间
// return statuscode
func (e *LineUser) GetUserInfo(userId int, data *dto.LineUserAppResp) int {
	user := models.LineUser{}
	userSetting := models.LineUserSetting{}

	if err := e.Orm.Model(&user).Where("id = ?", userId).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return statuscode.DataError
		}

		return statuscode.ServerError
	}

	e.Orm.Model(&userSetting).Where("user_id = ?", userId).First(&userSetting)

	if user.ExpirationTime != nil {
		data.ExpirationTimeUnix = user.ExpirationTime.UnixNano() / int64(time.Millisecond)
	}
	data.MinOrderAmount = userSetting.MinOrderAmount

	return statuscode.OK
}
