package appservice

import (
	"errors"
	"fmt"
	"go-admin/app/admin/models"
	adminservice "go-admin/app/admin/service"
	"go-admin/app/admin/service/dto"
	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"
	cDto "go-admin/common/dto"
	"go-admin/common/helper"
	statuscode "go-admin/common/status_code"
	"go-admin/pkg/utility"
	"math/rand"
	"time"

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

type MemberRenwalLog struct {
	service.Service
}

// 分页查询续期记录
func (e *MemberRenwalLog) GetPage(userId int, req *dto.MemberRenwalLogPageAppReq, list *[]dto.MemberRenwalLogResp, count *int64) int {
	var data models.MemberRenwaLog
	var datas []models.MemberRenwaLog
	resp := dto.MemberRenwalLogResp{}
	renwalIds := make([]int, 0)
	renwals := make([]models.MemberRenwalConfig, 0)

	err := e.Orm.Model(&data).
		Where("user_id = ? AND status !=?", userId, memberrenwallogstatus.EXPIRED).
		Scopes(
			cDto.MakeCondition(req.GetNeedSearch()),
			cDto.Paginate(req.GetPageSize(), req.GetPageIndex()),
		).
		Find(&datas).Limit(-1).Offset(-1).
		Count(count).Error

	if err != nil {
		return statuscode.ServerError
	}

	for _, v := range datas {
		if utility.ContainsInt(renwalIds, v.RenwalId) {
			continue
		}
		renwalIds = append(renwalIds, v.RenwalId)
	}

	e.Orm.Model(&models.MemberRenwalConfig{}).Where("id IN (?)", renwalIds).Find(&renwals)

	for _, item := range datas {
		copier.Copy(&resp, item)

		if item.PaymentTime != nil {
			resp.PaymentTimeUnix = item.PaymentTime.UnixNano() / int64(time.Millisecond)
		}

		resp.ExpirationTimeUnix = item.ExpirationTime.UnixNano() / int64(time.Millisecond)

		if req.Language != "zh_CN" {
			for _, v := range renwals {
				if v.Id == item.RenwalId {
					resp.RenwalName = v.PackageNameEn
				}
			}
		}

		*list = append(*list, resp)
	}

	return statuscode.OK
}

// 续期订单
func (e *MemberRenwalLog) Renwal(req *dto.MemberRenwalCreateAppReq, data *dto.MemberRenwalCreateAppResp) int {
	var renwalConfig models.MemberRenwalConfig
	renwalLog := models.MemberRenwaLog{}
	now := time.Now()
	configService := adminservice.SysConfig{Service: e.Service}
	configDict := map[string]dto.GetSysConfigByKEYForServiceResp{}
	expirationVal := 30
	var toAddress string

	configService.GetWithKeys(&[]string{"member_renwal_expiration_val", "member_receive_address"}, &configDict)

	if dict, ok := configDict["member_renwal_expiration_val"]; ok && dict.ConfigValue != "" {
		expirationVal = utility.ToInt(dict.ConfigValue)
	}

	if dict, ok := configDict["member_receive_address"]; !ok || dict.ConfigValue == "" {
		logger.Errorf("钱包收款地址不存在")
		return statuscode.ServerError
	} else if dict.ConfigValue != "" {
		toAddress = dict.ConfigValue
	}

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

		logger.Errorf("查询套餐详情失败,err:", err)
		return statuscode.ServerError
	}

	if renwalConfig.Status != memberrenwalconfigstatus.ENABLE {
		return statuscode.RenwalConfigDisabled
	}

	price := renwalConfig.OriginalPrice

	if renwalConfig.DiscountPrice.Cmp(decimal.Zero) > 0 {
		price = renwalConfig.DiscountPrice
	}

	amount, err := generateUniqueAmount(price, 3, 5, expirationVal)

	if err != nil {
		logger.Errorf("生成唯一金额失败,userId：%v err:%v", req.UserId, err)
		return statuscode.ServerError
	}
	renwalLog.RenwalId = req.ConfigId
	renwalLog.UserId = req.UserId
	renwalLog.RenwalName = renwalConfig.PackageName
	renwalLog.RenwalDuration = renwalConfig.DurationDay
	renwalLog.Status = memberrenwallogstatus.PENDING
	renwalLog.ExpirationTime = now.Add(time.Minute * time.Duration(expirationVal))
	renwalLog.Coin = "USDT"
	renwalLog.PayableAmount = amount
	renwalLog.NetworkName = "TRC20"
	renwalLog.ReceiveAddress = toAddress

	if err := e.Orm.Model(&models.MemberRenwaLog{}).Create(&renwalLog).Error; err != nil {
		logger.Errorf("创建续费记录失败,userId：%v err:%v", req.UserId, err)
		return statuscode.ServerError
	}
	data.Amount = renwalLog.PayableAmount
	data.ExpirationTimeUnix = renwalLog.ExpirationTime.UnixNano() / int64(time.Millisecond)
	data.NetworkName = renwalLog.NetworkName
	data.ToAddress = renwalLog.ReceiveAddress

	return statuscode.OK
}

// 生成唯一的金额
// base: 基础金额
// decimalPlaces: 小数位数
// maxRetries: 最大尝试次数
// expirationVal: 过期时间（分钟）
func generateUniqueAmount(base decimal.Decimal, decimalPlaces int, maxRetries int, expirationVal int) (decimal.Decimal, error) {
	// 尝试生成最大次数为 maxRetries
	for i := 0; i < maxRetries; i++ {
		amount := GenerateRandomDecimal(base, decimalPlaces)

		// 如果没有重复，返回生成的金额
		err := helper.DefaultRedis.SetAdd(fmt.Sprintf(rediskey.OrderAmount, amount), "1", time.Duration(expirationVal))
		if err == nil {
			// 插入成功，返回该金额
			return amount, nil
		}

		// key 已存在，继续尝试
		if err.Error() == "key已存在" {
			fmt.Println("金额已存在，重试生成...")
			continue
		}
		// 成功，返回生成的金额
		return amount, nil
	}

	// 超过最大重试次数，增加小数位数并递归
	return generateUniqueAmount(base, decimalPlaces+1, maxRetries, expirationVal)
}

func GenerateRandomDecimal(base decimal.Decimal, decimalPlaces int) decimal.Decimal {
	// 生成一个随机的小数部分，最大为 1，但小数位数不超过指定的位数
	rand.Seed(time.Now().UnixNano())
	randomFraction := rand.Float64()

	// 将随机小数调整到指定的小数位数
	randomDecimal := decimal.NewFromFloat(randomFraction).Round(int32(decimalPlaces))

	// 将原始的 decimal 加上随机小数部分
	result := base.Add(randomDecimal)

	// 返回最终结果，确保精确到指定的小数位数
	return result.Round(int32(decimalPlaces))
}
