package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"pure-admin/global"
	"pure-admin/initialize/api"
	"pure-admin/model"
	"pure-admin/model/request"
	"pure-admin/model/response"
	"pure-admin/utils"
	"time"
)

func GetBillData(platform string, info *request.BillSearch) (error, response.BillData) {
	var (
		err    error
		money  float64
		total  int64
		result response.BillData
	)
	info.Status = 1
	_, money, total = getBillAmount(platform, info)
	result.Income = money
	result.IncomeCount = total

	info.Status = 2
	_, money, total = getBillAmount(platform, info)
	result.Expend = money
	result.ExpendCount = total
	return err, result
}

func getBillAmount(platform string, info *request.BillSearch) (error, float64, int64) {
	var (
		err    error
		result float64
		total  int64
	)
	db := global.MG_DB.Model(&model.Bill{})
	if platform != "" {
		db = db.Where("platform = ?", platform)
	}
	if info.StartTime != "" {
		db = db.Where("created_at >= ?", info.StartTime)
	}
	if info.EndTime != "" {
		if len(info.EndTime) == 10 {
			tmp, _ := time.ParseInLocation(utils.DateFormat, info.EndTime, time.Local)
			info.EndTime = tmp.AddDate(0, 0, 1).Format(utils.DateFormat)
		}
		db = db.Where("created_at < ?", info.EndTime)
	}
	if info.Status != 0 {
		db = db.Where("`status` = ?", info.Status)
	}
	if info.Receipt != 0 {
		db = db.Where("receipt = ?", info.Receipt)
	}
	_ = db.Count(&total).Error
	err = db.Select("IFNULL(SUM(`price`),0)").Scan(&result).Error
	if err != nil {
		return errors.New("获取失败"), result, total
	}
	return nil, result, total
}

func GetBillList(platform string, info *request.BillSearch) (error, []model.BillList, int64) {
	var (
		err    error
		total  int64
		result []model.BillList
	)
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
	db := global.MG_DB.Model(&model.Bill{})
	if platform != "" {
		db = db.Where("platform = ?", platform)
	}
	if info.StartTime != "" {
		db = db.Where("created_at >= ?", info.StartTime)
	}
	if info.EndTime != "" {
		if len(info.EndTime) == 10 {
			tmp, _ := time.ParseInLocation(utils.DateFormat, info.EndTime, time.Local)
			info.EndTime = tmp.AddDate(0, 0, 1).Format(utils.DateFormat)
		}
		db = db.Where("created_at < ?", info.EndTime)
	}
	if info.Status != 0 {
		db = db.Where("`status` = ?", info.Status)
	}
	if info.Receipt != 0 {
		db = db.Where("receipt = ?", info.Receipt)
	}
	_ = db.Count(&total).Error
	err = db.Select("id,user_id,title,order_id,price,`status`,remark,platform,transaction_id,created_at,updated_at").Order("id desc").Offset(offset).Limit(limit).Find(&result).Error
	if err != nil {
		return errors.New("获取失败"), result, total
	}
	var (
		uuids   []string
		userMap map[string]model.UserView
	)
	for _, bill := range result {
		uuids = append(uuids, bill.UserID)
	}
	userMap, err = getUserViewMap(uuids...)
	if err != nil {
		return errors.New("get user info fail"), result, total
	}
	for i := 0; i < len(result); i++ {
		if val, ok := userMap[result[i].UserID]; ok {
			result[i].User = val
		}
	}
	return err, result, total
}

func GetInfluenceWithdrawalData(info *request.WithdrawalSearch) (error, response.InfluenceWithdrawalData) {
	var (
		err    error
		money  float64
		total  int64
		result response.InfluenceWithdrawalData
	)
	// 网红账户总额
	_ = global.MG_DB.Model(&model.Wallet{}).Where("platform = 'influencer'").Select("IFNULL(SUM(`balance`),0)").Scan(&result.Balance).Error
	_, money, total = getWithdrawalAmount("1", info)
	result.Unexamined = money
	result.UnexaminedCount = total
	return err, result
}
func getWithdrawalAmount(platform string, info *request.WithdrawalSearch) (error, float64, int64) {
	var (
		err    error
		result float64
		total  int64
	)
	db := global.MG_DB.Model(&model.Withdrawal{})
	if platform != "" {
		db = db.Where("platform = ?", platform)
	}
	if info.StartTime != "" {
		db = db.Where("created_at >= ?", info.StartTime)
	}
	if info.EndTime != "" {
		if len(info.EndTime) == 10 {
			tmp, _ := time.ParseInLocation(utils.DateFormat, info.EndTime, time.Local)
			info.EndTime = tmp.AddDate(0, 0, 1).Format(utils.DateFormat)
		}
		db = db.Where("created_at < ?", info.EndTime)
	}
	if info.CheckStatus != "" {
		db = db.Where("check_status = ?", info.CheckStatus)
	}
	_ = db.Count(&total).Error
	err = db.Select("IFNULL(SUM(`amount`),0)").Scan(&result).Error
	if err != nil {
		return errors.New("获取失败"), result, total
	}
	return nil, result, total
}
func GetWithdrawalList(platform string, info *request.WithdrawalSearch) (error, []model.Withdrawal, int64) {
	var (
		err    error
		total  int64
		result []model.Withdrawal
	)
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
	db := global.MG_DB.Model(&model.Withdrawal{})
	if platform != "" {
		db = db.Where("platform = ?", platform)
	}
	if info.StartTime != "" {
		db = db.Where("created_at >= ?", info.StartTime)
	}
	if info.EndTime != "" {
		if len(info.EndTime) == 10 {
			tmp, _ := time.ParseInLocation(utils.DateFormat, info.EndTime, time.Local)
			info.EndTime = tmp.AddDate(0, 0, 1).Format(utils.DateFormat)
		}
		db = db.Where("created_at < ?", info.EndTime)
	}
	if info.CheckStatus != "" {
		db = db.Where("check_status = ?", info.CheckStatus)
	}
	_ = db.Count(&total).Error
	err = db.Order("id desc").Offset(offset).Limit(limit).Find(&result).Error
	if err != nil {
		return errors.New("获取失败"), result, total
	}
	return err, result, total
}
func ExamineWithdrawal(platform string, info *request.ExamineWithdrawal) error {
	var (
		err           error
		withdrawal    model.Withdrawal
		notifyContent string
	)
	err = global.MG_DB.Model(&model.Withdrawal{}).Where("platform = ? AND flow_no = ?", platform, info.FlowNo).First(&withdrawal).Error
	if err != nil {
		return err
	}
	if withdrawal.CheckStatus != "0" || withdrawal.Status != "0" {
		return errors.New("withdrawal reply status error")
	}

	tx := global.MG_DB.Begin()
	if info.CheckStatus == "1" { // 审核通过
		err = tx.Model(&model.Withdrawal{}).Where("id = ?", withdrawal.ID).Updates(map[string]interface{}{"check_status": info.CheckStatus, "check_time": time.Now()}).Error
		if err != nil {
			tx.Rollback()
			return err
		}
		notifyContent = "您的提现申请已通过!"
	} else if info.CheckStatus == "2" { // 审核不通过
		err = tx.Model(&model.Withdrawal{}).Where("id = ?", withdrawal.ID).Updates(map[string]interface{}{"check_status": info.CheckStatus, "remark": info.Remark, "check_time": time.Now()}).Error
		if err != nil {
			tx.Rollback()
			return err
		}
		notifyContent = "您的提现申请未通过!"
	} else {
		tx.Rollback()
		return errors.New("check status error")
	}
	// 更改申请账号钱包状态
	err = tx.Model(&model.Wallet{}).Where("user_id = ?", withdrawal.CreateBy).UpdateColumn("state", "0").Error
	if err != nil {
		tx.Rollback()
		return errors.New("update wallet state error")
	}
	tx.Commit()
	if platform == "influencer" {
		// 发送消息
		_ = createOrUpdateNotify(request.CreateNotify{
			UserId:       withdrawal.CreateBy,
			RelationType: "1",
			RelationId:   withdrawal.FlowNo,
			Title:        notifyContent,
		})
	}
	return err
}

func UpdateWithdrawalStatus(info *request.TransferResult) error {
	// 开始交易
	for {
		ok, err := global.MG_REDIS.SetNX("withdrawal-"+info.FlowNo, "used", 10*time.Second).Result()
		if ok && err == nil {
			// 获取成功
			break
		}
	}
	defer func() {
		// 释放锁
		_, _ = utils.RedisDel("withdrawal-" + info.FlowNo)
	}()
	var (
		err           error
		notifyContent string
		withdrawal    model.Withdrawal
	)
	err = global.MG_DB.Model(&model.Withdrawal{}).Where("flow_no = ?", info.FlowNo).First(&withdrawal).Error
	if err != nil {
		return err
	}
	if withdrawal.Status != "0" {
		err = errors.New("repeat operation")
		return err
	}
	tx := global.MG_DB.Begin()
	err = tx.Model(&model.Withdrawal{}).Where("id = ?", withdrawal.ID).UpdateColumns(map[string]interface{}{"status": info.Status, "remark": info.Remark}).Error
	if err != nil {
		tx.Rollback()
		return err
	}
	// 恢复网红用户钱包状态
	if withdrawal.Platform == "1" {
		err = tx.Model(&model.Wallet{}).Where("user_id = ?", withdrawal.CreateBy).UpdateColumn("state", "0").Error
		if err != nil {
			tx.Rollback()
			return err
		}
	}
	if info.Status == "1" {
		notifyContent = "您的提现申请已通过!"
	} else { // 审核不通过,将金额返还至用户余额
		notifyContent = "您的提现申请未通过!"
		err = tx.Model(&model.Wallet{}).Where("user_id = ?", withdrawal.CreateBy).UpdateColumn("balance", gorm.Expr("balance + ?", withdrawal.Amount)).Error
		if err != nil {
			tx.Rollback()
			return err
		}
	}
	tx.Commit()
	if withdrawal.Platform == "1" {
		// 发送消息
		_ = createOrUpdateNotify(request.CreateNotify{
			UserId:       withdrawal.CreateBy,
			RelationType: "1",
			RelationId:   withdrawal.FlowNo,
			Title:        notifyContent,
		})
	}
	return err
}

//func TransferWithdrawalRetry(platform string, info *request.RetryWithdrawal) error {
//	var (
//		err        error
//		transfer   request.TransferWithdrawal
//		withdrawal model.Withdrawal
//	)
//	err = global.MG_DB.Model(&model.Withdrawal{}).Where("platform = ? AND flow_no = ?", platform, info.FlowNo).First(&withdrawal).Error
//	if err != nil {
//		return err
//	}
//	if withdrawal.CheckStatus != "1" || withdrawal.Status != "2" {
//		return errors.New("withdrawal reply status error")
//	}
//	transfer.AccountType = withdrawal.AccountType
//	transfer.AccountName = withdrawal.Account
//	transfer.Account = withdrawal.Account
//	transfer.Amount = withdrawal.Amount
//	transfer.FlowNo = withdrawal.FlowNo
//	err, payout := transferWithdrawal(&transfer)
//	err = global.MG_DB.Model(&model.Withdrawal{}).Where("id = ?", withdrawal.ID).UpdateColumn("pay_id", payout.PayId).Error
//	if err != nil {
//		return err
//	}
//	return err
//}

func transferWithdrawal(info *request.TransferWithdrawal) (error, *api.PayUrlResponse) {
	var err error
	attach, _ := json.Marshal(map[string]string{"transactionId": info.FlowNo})
	params := api.PayoutRequest{
		Appid:      "bkb5918273465092837",
		Mchid:      "11000001",
		OutTradeNo: info.FlowNo,
		Attach:     string(attach),
		NotifyUrl:  global.MG_CONFIG.Paypal.NotifyUrl + "/base/payment/payback",
		Amount:     info.Amount,
		Currency:   "USD",
		PayChannel: info.AccountType,
		PaypalName: info.AccountName,
	}
	payoutOrder, err := global.PAY_CLIENT.PayoutsAppUrl(context.Background(), &params)
	if err != nil {
		fmt.Println(err.Error())
		return errors.New("open " + info.AccountType + " failed"), nil
	}
	return err, payoutOrder
}

func GetSellerWithdrawalData(info *request.WithdrawalSearch) (error, response.SellerWithdrawalData) {
	var (
		err    error
		money  float64
		total  int64
		result response.SellerWithdrawalData
	)
	_ = global.MG_DB.Model(&model.Wallet{}).Where("platform = 'seller'").Select("IFNULL(SUM(`balance`),0)").Scan(&result.Balance).Error
	_ = global.MG_DB.Model(&model.Wallet{}).Where("platform = 'seller'").Select("IFNULL(SUM(`fund`),0)").Scan(&result.Fund).Error

	_, money, total = getWithdrawalAmount("2", info)
	result.Unexamined = money
	result.UnexaminedCount = total
	return err, result
}

func getBillFundAmount(platform string, info *request.BillFundSearch) (error, float64, int64) {
	var (
		err    error
		result float64
		total  int64
	)
	db := global.MG_DB.Model(&model.BillFund{})
	if platform != "" {
		db = db.Where("platform = ?", platform)
	}
	if info.UserID != "" {
		db = db.Where("user_id = ?", info.UserID)
	}
	if info.TransactionType != 0 {
		db = db.Where("transaction_type = ?", info.TransactionType)
	}
	if info.StartTime != "" {
		db = db.Where("created_at >= ?", info.StartTime)
	}
	if info.EndTime != "" {
		if len(info.EndTime) == 10 {
			tmp, _ := time.ParseInLocation(utils.DateFormat, info.EndTime, time.Local)
			info.EndTime = tmp.AddDate(0, 0, 1).Format(utils.DateFormat)
		}
		db = db.Where("created_at < ?", info.EndTime)
	}
	if info.Status != 0 {
		db = db.Where("`status` = ?", info.Status)
	}
	_ = db.Count(&total).Error
	err = db.Select("SUM(`price`)").Scan(&result).Error
	if err != nil {
		return errors.New("获取失败"), result, total
	}
	return nil, result, total
}

func GetSellerFundData(info *request.BillFundSearch) (error, response.SellerFundData) {
	var (
		err    error
		money  float64
		total  int64
		result response.SellerFundData
	)
	_, money, total = getBillFundAmount("seller", info)
	result.Expend = money
	result.ExpendCount = total

	_, money, total = getMissionFundLockAmount(&request.SearchMissionFund{})
	result.Lock = money
	result.LockCount = total
	return err, result
}

func GetSellerFundList(platform string, info *request.BillFundSearch) (error, []model.SellerBillFund, int64) {
	var (
		err          error
		total        int64
		billFundList []model.BillFund
		result       []model.SellerBillFund
	)
	err, billFundList, total = getBillFundList(platform, info)
	if err != nil {
		return err, result, 0
	}
	var (
		sellerIds []string
		claimNos  []string
	)
	for i := 0; i < len(billFundList); i++ {
		sellerIds = append(sellerIds, billFundList[i].UserID)
		claimNos = append(claimNos, billFundList[i].RelatedId)
		result = append(result, model.SellerBillFund{
			UserID:          billFundList[i].UserID,
			TransactionType: billFundList[i].TransactionType,
			TransactionId:   billFundList[i].TransactionId,
			Price:           billFundList[i].Price,
			Remark:          billFundList[i].Remark,
			RelatedId:       billFundList[i].RelatedId,
			Status:          billFundList[i].Status,
			MG_MODEL:        billFundList[i].MG_MODEL,
		})
	}
	storeMap, _ := getSellerStoreMap(sellerIds)
	claimMap, _ := getMissionClaimViewMap(claimNos)
	for i := 0; i < len(result); i++ {
		if val, ok := storeMap[result[i].UserID]; ok {
			result[i].Seller = val
		}
		if val, ok := claimMap[result[i].RelatedId]; ok {
			result[i].Mission = val
		}
	}
	return err, result, total
}

func getBillFundList(platform string, info *request.BillFundSearch) (error, []model.BillFund, int64) {
	var (
		err    error
		total  int64
		result []model.BillFund
	)
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
	db := global.MG_DB.Model(&model.BillFund{})
	if platform != "" {
		db = db.Where("platform = ?", platform)
	}
	if info.StartTime != "" {
		db = db.Where("created_at >= ?", info.StartTime)
	}
	if info.EndTime != "" {
		if len(info.EndTime) == 10 {
			tmp, _ := time.ParseInLocation(utils.DateFormat, info.EndTime, time.Local)
			info.EndTime = tmp.AddDate(0, 0, 1).Format(utils.DateFormat)
		}
		db = db.Where("created_at < ?", info.EndTime)
	}
	if info.Status != 0 {
		db = db.Where("`status` = ?", info.Status)
	}
	_ = db.Count(&total).Error
	err = db.Order("id desc").Offset(offset).Limit(limit).Find(&result).Error
	if err != nil {
		return errors.New("获取失败"), result, total
	}
	return err, result, total
}

func GetBkbWithdrawalData(info *request.WithdrawalSearch) (error, response.BkbData) {
	var (
		err    error
		money  float64
		total  int64
		result response.BkbData
	)

	_, money, total = getWithdrawalAmount("", info)
	result.Unexamined = money
	result.UnexaminedCount = total
	return err, result
}

func GetBkbFundData(info *request.BillFundSearch) (error, response.AdminFundData) {
	var (
		err    error
		money  float64
		total  int64
		result response.AdminFundData
	)
	info.TransactionType = 1
	_, money, total = getBillFundAmount("bkb", info)
	result.Expend = money
	result.ExpendCount = total

	info.TransactionType = 2
	_, money, total = getBillFundAmount("bkb", info)
	result.Recharge = money
	result.RechargeCount = total
	return err, result
}

func GetBkbFundList(platform string, info *request.BillFundSearch) (error, []model.AdminBillFund, int64) {
	var (
		err          error
		total        int64
		billFundList []model.BillFund
		result       []model.AdminBillFund
	)
	err, billFundList, total = getBillFundList(platform, info)
	if err != nil {
		return err, result, 0
	}
	for i := 0; i < len(billFundList); i++ {
		result = append(result, model.AdminBillFund{
			UserID:          billFundList[i].UserID,
			TransactionType: billFundList[i].TransactionType,
			TransactionId:   billFundList[i].TransactionId,
			Price:           billFundList[i].Price,
			Remark:          billFundList[i].Remark,
			RelatedId:       billFundList[i].RelatedId,
			Status:          billFundList[i].Status,
			MG_MODEL:        billFundList[i].MG_MODEL,
		})
	}
	return err, result, total
}