package binanceservice

import (
	"context"
	"errors"
	"fmt"
	DbModels "go-admin/app/admin/models"
	"go-admin/app/admin/service/dto"
	"go-admin/common/const/rediskey"
	"go-admin/common/global"
	"go-admin/common/helper"
	"go-admin/models"
	"strings"
	"time"

	"github.com/bytedance/sonic"
	log "github.com/go-admin-team/go-admin-core/logger"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
)

/*
判断合约触发
*/
func JudgeFuturesPrice(tradeSet models.TradeSet) {
	preOrderVal, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.PreFutOrderList, global.EXCHANGE_BINANCE))
	db := GetDBConnection()

	if len(preOrderVal) == 0 {
		return
	}
	futApi := FutRestApi{}

	for _, item := range preOrderVal {
		preOrder := dto.PreOrderRedisList{}
		if err := sonic.Unmarshal([]byte(item), &preOrder); err != nil {
			log.Error("反序列化失败")
			continue
		}

		if preOrder.Symbol != tradeSet.Coin+tradeSet.Currency {
			continue
		}

		orderPrice, _ := decimal.NewFromString(preOrder.Price)
		tradePrice, _ := decimal.NewFromString(tradeSet.LastPrice)

		if orderPrice.Cmp(decimal.Zero) == 0 || tradePrice.Cmp(decimal.Zero) == 0 {
			continue
		}

		//多
		if (strings.ToUpper(preOrder.Site) == "BUY" && orderPrice.Cmp(tradePrice) >= 0) ||
			(strings.ToUpper(preOrder.Site) == "SELL" && orderPrice.Cmp(tradePrice) <= 0) {
			futTriggerOrder(db, &preOrder, item, futApi)
		}
	}
}

// 分布式锁下单
func futTriggerOrder(db *gorm.DB, v *dto.PreOrderRedisList, item string, futApi FutRestApi) {
	lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, v.ApiId, v.Symbol), 200, 5, 100*time.Millisecond)

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

		key := fmt.Sprintf(rediskey.PreFutOrderList, global.EXCHANGE_BINANCE)
		preOrder := DbModels.LinePreOrder{}
		if err := db.Where("id = ?", v.Id).First(&preOrder).Error; err != nil {
			log.Error("获取预下单失败", err)

			if errors.Is(err, gorm.ErrRecordNotFound) {
				log.Error("不存在待触发主单", item)
				helper.DefaultRedis.LRem(key, item)
			}

			return
		}

		hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)

		if !hasrecord {
			log.Debug("预下单缓存中不存在", item)
			return
		}

		price, _ := decimal.NewFromString(v.Price)
		num, _ := decimal.NewFromString(preOrder.Num)

		if price.Cmp(decimal.Zero) == 0 {
			log.Error("价格不能为0")
			return
		}

		params := FutOrderPlace{
			ApiId:            v.ApiId,
			Symbol:           v.Symbol,
			Side:             v.Site,
			OrderType:        preOrder.MainOrderType,
			SideType:         preOrder.MainOrderType,
			Price:            price,
			Quantity:         num,
			NewClientOrderId: v.OrderSn,
		}
		preOrderVal, _ := sonic.MarshalString(&v)

		if err := futApi.OrderPlaceLoop(db, params, 3); err != nil {
			log.Error("下单失败", v.Symbol, " err:", err)
			err := db.Model(&DbModels.LinePreOrder{}).Where("id =? and status='0'", preOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error

			if err != nil {
				log.Error("更新预下单状态失败")
			}

			if preOrderVal != "" {
				if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
					log.Error("删除redis 预下单失败:", err)
				}
			}

			return
		}

		if preOrderVal != "" {
			if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
				log.Error("删除redis 预下单失败:", err)
			}
		}

		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", preOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil {
			log.Error("更新预下单状态失败 ordersn:", preOrder.OrderSn, " status:1")
		}

		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status ='0'", preOrder.Id).Updates(map[string]interface{}{"status": "1"}).Error; err != nil {
			log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1")
		}

		return
	} else {
		log.Error("获取锁失败")

		return
	}
}

// 判断是否触发合约减仓
func JudgeFuturesReduce(trade models.TradeSet) {
	key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE)
	reduceVal, _ := helper.DefaultRedis.GetAllList(key)

	if len(reduceVal) == 0 {
		return
	}

	db := GetDBConnection()
	futApi := FutRestApi{}
	setting, err := GetSystemSetting(db)

	if err != nil {
		log.Error("获取系统设置失败")
		return
	}

	for _, item := range reduceVal {
		reduceOrder := ReduceListItem{}
		if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil {
			log.Error("反序列化失败")
			continue
		}

		if reduceOrder.Symbol == trade.Coin+trade.Currency {
			orderPrice := reduceOrder.Price
			tradePrice, _ := decimal.NewFromString(trade.LastPrice)
			//买入
			if orderPrice.Cmp(decimal.Zero) > 0 &&
				tradePrice.Cmp(decimal.Zero) > 0 &&
				((strings.ToUpper(reduceOrder.Side) == "SELL" && orderPrice.Cmp(tradePrice) >= 0) ||
					(strings.ToUpper(reduceOrder.Side) == "BUY" && orderPrice.Cmp(tradePrice) <= 0)) {

				FuturesReduceTrigger(db, reduceOrder, futApi, setting, key, item)
			}
		}
	}
}

// 触发合约减仓
func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRestApi, setting DbModels.LineSystemSetting, key, item string) {
	tradeSet, _ := GetTradeSet(reduceOrder.Symbol, 1)

	if tradeSet.LastPrice == "" {
		return
	}

	lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, reduceOrder.ApiId, reduceOrder.Symbol), 20, 5, 100*time.Millisecond)

	if ok, err := lock.AcquireWait(context.Background()); err != nil {
		log.Error("获取锁失败", err)
		return
	} else if ok {
		defer lock.Release()
		takeOrders := make([]DbModels.LinePreOrder, 0)
		if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =1 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil {
			log.Error("查询止盈单失败")
			return
		}

		hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)

		if !hasrecord {
			log.Debug("减仓缓存中不存在", item)
			return
		}

		apiInfo, _ := GetApiInfo(reduceOrder.ApiId)

		if apiInfo.Id == 0 {
			log.Error("现货减仓 查询api用户不存在")
			return
		}
		for _, takeOrder := range takeOrders {
			err := CancelFutOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)

			if err != nil {
				log.Error("合约止盈撤单失败", err)
				return
			}
		}

		price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
		num := reduceOrder.Num.Truncate(int32(tradeSet.AmountDigit))
		var positionSide string

		if reduceOrder.Side == "BUY" {
			positionSide = "SHORT"
		} else {
			positionSide = "LONG"
		}

		// params := FutOrderPlace{
		// 	ApiId:            reduceOrder.ApiId,
		// 	Side:             reduceOrder.Side,
		// 	OrderType:        "STOP",
		// 	Symbol:           reduceOrder.Symbol,
		// 	Price:            price,
		// 	StopPrice:        price,
		// 	Quantity:         num,
		// 	NewClientOrderId: reduceOrder.OrderSn,
		// }

		if err := futApi.ClosePositionLoop(reduceOrder.Symbol, reduceOrder.OrderSn, num, reduceOrder.Side, positionSide, apiInfo, "LIMIT", "0", price, 3); err != nil {
			log.Errorf("合约减仓挂单失败 id：%s err:%v", reduceOrder.Id, err)

			if err2 := db.Model(&DbModels.LinePreOrder{}).
				Where("id = ? AND status =0", reduceOrder.Id).
				Updates(map[string]interface{}{"status": 2, "desc": err.Error()}).Error; err2 != nil {
				log.Errorf("合约减仓更新状态失败 id：%s err:%v", reduceOrder.Id, err2)
			}
		} else {
			if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", reduceOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil {
				log.Error("更新减仓单状态失败 ordersn:", reduceOrder.OrderSn, " status:1")
			}

			if err := db.Model(&DbModels.LinePreOrder{}).
				Where("id = ? AND status =0", reduceOrder.Id).Updates(map[string]interface{}{"status": 1}).Error; err != nil {
				log.Errorf("合约减仓更新状态失败 id：%s err:%v", reduceOrder.Id, err)
			}
		}

		if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
			log.Errorf("合约减仓 删除缓存失败 id:%v err:%v", reduceOrder.Id, err)
		}
	} else {
		log.Error("获取锁失败")
	}
}

// 判断合约加仓
func JudgeFutAddPosition(trade models.TradeSet) {
	key := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE)
	preOrderVal, _ := helper.DefaultRedis.GetAllList(key)
	db := GetDBConnection()

	if len(preOrderVal) == 0 {
		return
	}

	futApi := FutRestApi{}
	for _, item := range preOrderVal {
		preOrder := AddPositionList{}
		if err := sonic.Unmarshal([]byte(item), &preOrder); err != nil {
			log.Error("反序列化失败")
			continue
		}

		if preOrder.Symbol == trade.Coin+trade.Currency {
			orderPrice := preOrder.Price
			tradePrice, _ := decimal.NewFromString(trade.LastPrice)

			if orderPrice.Cmp(decimal.Zero) == 0 || tradePrice.Cmp(decimal.Zero) == 0 {
				continue
			}

			//多
			if (strings.ToUpper(preOrder.Side) == "BUY" && orderPrice.Cmp(tradePrice) >= 0) ||
				(strings.ToUpper(preOrder.Side) == "SELL" && orderPrice.Cmp(tradePrice) <= 0) {
				FutAddPositionTrigger(db, &preOrder, item, futApi)
			}
		}
	}
}

// 合约加仓触发
func FutAddPositionTrigger(db *gorm.DB, v *AddPositionList, item string, futApi FutRestApi) {
	lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, v.ApiId, v.Symbol), 20, 5, 100*time.Millisecond)

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

		setting, _ := GetSystemSetting(db)
		tradeSet, _ := GetTradeSet(v.Symbol, 1)

		if tradeSet.LastPrice == "" {
			log.Errorf("合约加仓触发 查询交易对失败 交易对：%s ordersn:%s", v.Symbol, v.OrderSn)
			return
		}

		key := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE)
		preOrder := DbModels.LinePreOrder{}
		if err := db.Where("id = ?", v.Id).First(&preOrder).Error; err != nil {
			log.Error("获取预下单失败", err)

			if errors.Is(err, gorm.ErrRecordNotFound) {
				log.Error("不存在待触发加仓主单", item)
				helper.DefaultRedis.LRem(key, item)
			}

			return
		}

		hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)

		if !hasrecord {
			log.Error("不存在待触发加仓主单", item)
			return
		}

		price := v.Price.Truncate(int32(tradeSet.PriceDigit))
		num, _ := decimal.NewFromString(preOrder.Num)

		if setting.AddPositionPremium.Cmp(decimal.Zero) > 0 {
			price = price.Mul(decimal.NewFromInt(1).Sub(setting.AddPositionPremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
		}

		params := FutOrderPlace{
			ApiId:            v.ApiId,
			Symbol:           v.Symbol,
			Side:             v.Side,
			OrderType:        "LIMIT",
			Price:            price,
			Quantity:         num.Truncate(int32(tradeSet.AmountDigit)),
			NewClientOrderId: v.OrderSn,
		}
		preOrderVal, _ := sonic.MarshalString(&v)

		if err := futApi.OrderPlaceLoop(db, params, 3); err != nil {
			log.Error("下单失败", v.Symbol, " err:", err)
			err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status =0", preOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error

			if err != nil {
				log.Error("下单失败后修改订单失败")
			}

			if preOrderVal != "" {
				if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
					log.Error("删除redis 预下单失败:", err)
				}
			}

			return
		}

		if preOrderVal != "" {
			if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
				log.Error("删除redis 预下单失败:", err)
			}
		}

		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", preOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil {
			log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1")
		}

		if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status ='0'", preOrder.Id).Updates(map[string]interface{}{"status": "1"}).Error; err != nil {
			log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1")
		}
		return
	} else {
		log.Error("获取锁失败")
		return
	}
}
