package appservice

import (
	"errors"
	"go-admin/app/admin/models"
	"go-admin/app/admin/service/dto"
	"go-admin/pkg/utility"
	"time"

	memberwithdrawallogstatus "go-admin/common/const/dicts/member_withdrawal_log_status"
	cDto "go-admin/common/dto"
	statuscode "go-admin/common/status_code"

	"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 MemberWithdrawalLog struct {
	service.Service
}

// 分页查询
func (e *MemberWithdrawalLog) GetPage(req *dto.MemberWithdrawalLogGetPageAppReq, list *[]dto.MemberWithdrawalLogResp, count *int64) int {
	var err error
	var data models.MemberWithdrawalLog
	var datas []models.MemberWithdrawalLog
	userIds := make([]int, 0)

	err = e.Orm.Model(&data).
		Scopes(
			cDto.MakeCondition(req.GetNeedSearch()),
			cDto.Paginate(req.GetPageSize(), req.GetPageIndex()),
		).
		Find(&datas).Limit(-1).Offset(-1).
		Count(count).Error
	if err != nil {
		e.Log.Errorf("MemberWithdrawalLogService GetPage error:%s \r\n", err)
		return statuscode.ServerError
	}

	for _, user := range datas {
		if !utility.ContainsInt(userIds, user.UserId) {
			userIds = append(userIds, user.UserId)
		}
	}

	userNames := make([]models.LineUser, 0)
	e.Orm.Model(&models.LineUser{}).Where("id in (?)", userIds).Select("id,nickname").Find(&userNames)

	for _, v := range datas {
		item := dto.MemberWithdrawalLogResp{}
		copier.Copy(&item, &v)

		for _, user := range userNames {
			if v.UserId == user.Id {
				item.NickName = user.Nickname
			}
		}

		item.CreateTimeUnix = v.CreatedAt.UnixNano() / int64(time.Millisecond)

		if v.ConfirmTime != nil {
			item.ConfirmTimeUnix = v.ConfirmTime.UnixNano() / int64(time.Millisecond)
		}

		*list = append(*list, item)
	}

	return statuscode.OK
}

// 用户提现申请
// return statuscode
func (e *MemberWithdrawalLog) Apply(req *dto.MemberWithdrawalLogApplyReq) int {
	// config:=adminservice.SysConfig{}
	var network models.LineCoinnetwork

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

		logger.Errorf("查询网络失败 网络id:%v err:%v", req.NetworkId, err)
		return statuscode.ServerError
	}

	if network.Status != 1 {
		return statuscode.NetworkUnAvailable
	}

	//小于最小提现金额
	if req.Amount.Cmp(network.MinWithdrawal) < 0 {
		return statuscode.BelowMinimum
	}

	fee := req.Amount.Mul(network.Fee.Div(decimal.NewFromInt(100))).Truncate(2)
	code := statuscode.OK
	log := models.MemberWithdrawalLog{
		UserId:      req.UserId,
		NetworkId:   req.NetworkId,
		NetworkName: network.NetworkName,
		Amount:      req.Amount,
		ToAddress:   req.ToAddress,
		Status:      memberwithdrawallogstatus.PENDING,
		Fee:         fee,
	}

	//事务
	e.Orm.Transaction(func(tx *gorm.DB) error {
		balance := models.MemberBalance{}

		if err := tx.Model(&balance).Where("user_id = ?", req.UserId).First(&balance).Error; err != nil {
			code = statuscode.InsufficientFunds
			return err
		}

		frozenAmount := fee.Add(req.Amount)
		if frozenAmount.Cmp(balance.FreeAmount) > 0 {
			code = statuscode.InsufficientFunds
			return errors.New("余额不足")
		}

		balance.FreeAmount = balance.FreeAmount.Sub(frozenAmount)
		balance.FrozenAmount = balance.FrozenAmount.Add(frozenAmount)

		if err := tx.Create(&log).Error; err != nil {
			code = statuscode.ServerError
			logger.Errorf("创建提现记录失败 userid:%d, err:%v", req.UserId, err)

			return err
		}

		if err := tx.Model(&balance).Updates(map[string]interface{}{
			"free_amount":   balance.FreeAmount,
			"frozen_amount": balance.FrozenAmount,
		}).Error; err != nil {
			code = statuscode.ServerError
			logger.Errorf("更新余额失败 userid:%v err：%v", req.UserId, err)
			return err
		}

		return nil
	})

	return code
}

// 取消提现
func (e *MemberWithdrawalLog) Canceled(req *dto.MemberWithdrawalLogCancelReq, userId int) int {
	code := statuscode.OK

	e.Orm.Transaction(func(tx *gorm.DB) error {
		var log models.MemberWithdrawalLog
		if err := tx.Model(&log).Where("id = ? AND user_id =?", req.Id, userId).First(&log).Error; err != nil {
			code = statuscode.DataError
			return err
		}

		if log.Status != memberwithdrawallogstatus.PENDING {
			code = statuscode.CanNotCancel
			return errors.New("提现记录状态错误")
		}

		log.Status = memberwithdrawallogstatus.CANCELED
		totalAmount := log.Amount.Add(log.Fee)

		if err := tx.Exec("UPDATE member_balance SET free_amount=free_amount+?,frozen_amount=frozen_amount-? WHERE user_id =?", totalAmount, totalAmount, log.UserId).Error; err != nil {
			code = statuscode.ServerError
			logger.Errorf("更新余额失败 提现订单:id userid:%v err：%v", req.Id, userId, err)
			return err
		}

		if err := tx.Model(&log).Update("status", log.Status).Error; err != nil {
			code = statuscode.ServerError
			logger.Errorf("更新提现订单状态失败 提现订单:id userid:%v err：%v", req.Id, userId, err)
			return err
		}
		return nil
	})

	return code
}
