package binanceservice

import (
	"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/positiondto"
	"go-admin/pkg/utility"
	"go-admin/services/commonservice"
	"go-admin/services/positionservice"
	"strings"
	"time"

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

type AddPosition struct {
	Db *gorm.DB
}

func GetRisk(futApi *FutRestApi, apiInfo *DbModels.LineApiUser, symbol string) []PositionRisk {
	for x := 0; x < 5; x++ {
		risks, _ := futApi.GetPositionV3(apiInfo, symbol)
		if len(risks) > 0 {
			return risks
		}
		time.Sleep(time.Millisecond * 200)
	}

	return []PositionRisk{}
}

// CancelFutClosePosition 撤销交易对指定方向的平仓单
// apiUserInfo 账户信息
// symbol 交易对
// side 购买反向
// positionSide 仓位方向
// side=BUY positionSide=SHORT 就是撤销平空委托
// side=BUY&positionSide=LONG是开多，
// side=SELL&positionSide=LONG是平多，
// side=SELL&positionSide=SHORT是开空，
// side=BUY&positionSide=SHORT是平空。
func CancelFutClosePosition(apiUserInfo DbModels.LineApiUser, symbol string, side string, positionSide string) error {
	//查询当前用户的委托订单
	client := GetClient(&apiUserInfo)
	resp, _, err := client.SendFuturesRequestAuth("/fapi/v1/openOrders", "GET", map[string]string{
		"symbol":     symbol,
		"recvWindow": "5000",
	})
	if err != nil {
		logger.Error("撤销平仓单时查询委托失败：", err)
		return err
	}
	var openOrders []OpenOrders
	sonic.Unmarshal(resp, &openOrders)
	orderIdList := make([]int, 0)
	for _, order := range openOrders {
		if order.Side == side && order.PositionSide == positionSide {
			orderIdList = append(orderIdList, order.OrderId)
		}
	}
	// 每次取 10 个元素
	batchSize := 10
	for i := 0; i < len(orderIdList); i += batchSize {
		end := i + batchSize
		if end > len(orderIdList) {
			end = len(orderIdList) // 避免越界
		}
		// 取出当前批次的元素
		batch := orderIdList[i:end]
		marshal, _ := sonic.Marshal(&batch)
		_, _, err = client.SendFuturesRequestAuth("/fapi/v1/batchOrders", "DELETE", map[string]string{
			"symbol":      symbol,
			"orderIdList": string(marshal),
			"recvWindow":  "5000",
		})
		if err != nil {
			return err
		}
	}
	return err
}

// CancelSpotClosePosition 取消现货平仓单(等同于撤销卖单)
// apiUserInfo: Api用户信息
// symbol: 交易对
func CancelSpotClosePosition(apiUserInfo *DbModels.LineApiUser, symbol string) error {

	return CancelSpotOrder(symbol, apiUserInfo, "SELL")
}

// 取消现货订单
func CancelSpotOrder(symbol string, apiUserInfo *DbModels.LineApiUser, side string) error {
	searchEndpoint := "/api/v3/openOrders"
	cencelEndpoint := "/api/v3/order"
	searchParams := map[string]interface{}{
		"symbol": symbol,
	}
	client := GetClient(apiUserInfo)
	resp, _, err := client.SendSpotAuth(searchEndpoint, "GET", searchParams)
	db := commonservice.GetDBConnection()

	if err != nil {
		if len(resp) > 0 {

		} else {
			logger.Error("查询现货当前下单失败:", err)
		}
	}

	var openOrders []map[string]interface{}
	err = sonic.Unmarshal(resp, &openOrders)
	if err != nil {
		return err
	}

	for _, order := range openOrders {
		if orderSymbol, ok := order["symbol"].(string); ok && orderSymbol == symbol {
			if orderSide, ok := order["side"].(string); ok && orderSide == side {
				orderId, ok := order["orderId"].(float64)

				if !ok {
					continue
				}

				orderSn, _ := order["clientOrderId"].(string)
				params := map[string]string{
					"symbol":  orderSymbol,
					"orderId": utility.Float64CutString(orderId, 0),
					// "cancelRestrictions": "ONLY_NEW",
				}
				_, _, err = client.SendSpotAuth(cencelEndpoint, "DELETE", params)

				if err != nil {
					logger.Error("撤销指定现货平仓单失败 ordersn:", orderSn, " orderId:", orderId, " err:", err)
				} else {
					if err := db.Model(&DbModels.LinePreOrder{}).Where("order_sn = ? and status !='9'", orderSn).Update("status", "4").Error; err != nil {
						log.Error("修改止盈单撤销状态失败:", err)
					}
				}
			}
		}
		return nil
	}
	return nil
}

// GetTargetSymbol 获取目标交易对信息
func (e *AddPosition) GetTargetSymbol(symbol string, symbolType int) (string, bool, DbModels.LineSymbol, error) {
	var targetSymbol string
	var notUsdt bool
	var symbolInfo DbModels.LineSymbol

	// 处理非 USDT 交易对
	if !strings.HasSuffix(symbol, "USDT") {
		notUsdt = true
		if err := e.Db.Model(&DbModels.LineSymbol{}).Where("symbol = ? AND type = ?", symbol, utility.IntToString(symbolType)).Find(&symbolInfo).Error; err != nil {
			return "", false, DbModels.LineSymbol{}, err
		}
		if symbolInfo.Id <= 0 {
			return "", false, DbModels.LineSymbol{}, fmt.Errorf("未找到交易对信息")
		}
		targetSymbol = symbolInfo.BaseAsset + "USDT"
	} else {
		targetSymbol = symbol
	}

	return targetSymbol, notUsdt, symbolInfo, nil
}

func (e *AddPosition) GetOrderInfo(req dto.ManuallyCover, symbol, orderType, site, status string) (DbModels.LinePreOrder, error) {
	var orderInfo DbModels.LinePreOrder
	if err := e.Db.Model(DbModels.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND site = ? AND status = ?", req.ApiId, symbol, orderType, site, status).Find(&orderInfo).Error; err != nil {
		return DbModels.LinePreOrder{}, err
	}
	if orderInfo.Id <= 0 {
		return DbModels.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
	}
	return orderInfo, nil
}

func (e *AddPosition) GetFutOrderInfo(req dto.ManuallyCover, symbol, orderType, status string) (DbModels.LinePreOrder, error) {
	var orderInfo DbModels.LinePreOrder
	if err := e.Db.Model(DbModels.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND status = ? AND cover_type = 2", req.ApiId, symbol, orderType, status).Find(&orderInfo).Error; err != nil {
		return DbModels.LinePreOrder{}, err
	}
	if orderInfo.Id <= 0 {
		return DbModels.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
	}
	return orderInfo, nil
}

// GetFutSpotOrderInfo 获取合约对现货的订单信息
func (e *AddPosition) GetFutSpotOrderInfo(req dto.ManuallyCover, symbol, orderType, status string) (DbModels.LinePreOrder, error) {
	var orderInfo DbModels.LinePreOrder
	if err := e.Db.Model(DbModels.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND status = ? AND cover_type = 3", req.ApiId, symbol, orderType, status).Find(&orderInfo).Error; err != nil {
		return DbModels.LinePreOrder{}, err
	}
	if orderInfo.Id <= 0 {
		return DbModels.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
	}
	return orderInfo, nil
}

// 主单平仓删除缓存
// mainOrderId 主单id
// symbolType 1现货 2合约
func MainClosePositionClearCache(mainId int, symbolType int) {
	if symbolType == 1 {
		keySpotStop := fmt.Sprintf(rediskey.SpotStopLossList, global.EXCHANGE_BINANCE)
		keySpotAddposition := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE)
		spotStopArray, _ := helper.DefaultRedis.GetAllList(keySpotStop)
		spotAddpositionArray, _ := helper.DefaultRedis.GetAllList(keySpotAddposition)
		var position positiondto.AddPositionList
		var stop dto.StopLossRedisList

		for _, item := range spotAddpositionArray {
			if err := sonic.Unmarshal([]byte(item), &position); err != nil {
				log.Error("MainClosePositionClearCache Unmarshal err:", err)
			}

			if position.MainId == mainId {
				helper.DefaultRedis.LRem(keySpotAddposition, item)
			}
		}

		for _, item := range spotStopArray {
			if err := sonic.Unmarshal([]byte(item), &stop); err != nil {
				log.Error("MainClosePositionClearCache Unmarshal err:", err)
			}

			if stop.MainId == mainId {
				helper.DefaultRedis.LRem(keySpotStop, item)
			}
		}

	} else {
		keyFutStop := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE)
		keyFutAddposition := fmt.Sprintf(rediskey.FuturesStopLossList, global.EXCHANGE_BINANCE)
		futAddpositionArray, _ := helper.DefaultRedis.GetAllList(keyFutStop)
		futStopArray, _ := helper.DefaultRedis.GetAllList(keyFutAddposition)
		var position positiondto.AddPositionList
		var stop dto.StopLossRedisList

		for _, item := range futAddpositionArray {
			if err := sonic.Unmarshal([]byte(item), &position); err != nil {
				log.Error("MainClosePositionClearCache Unmarshal err:", err)
			}

			if position.MainId == mainId {
				helper.DefaultRedis.LRem(keyFutAddposition, item)
			}
		}

		for _, item := range futStopArray {
			if err := sonic.Unmarshal([]byte(item), &stop); err != nil {
				log.Error("MainClosePositionClearCache Unmarshal err:", err)
			}

			if stop.MainId == mainId {
				helper.DefaultRedis.LRem(keyFutStop, item)
			}
		}
	}
}

// 查询订单
func getPreOrder(db *gorm.DB, orderSn interface{}) (*DbModels.LinePreOrder, error) {
	preOrder := &DbModels.LinePreOrder{}
	if err := db.Model(preOrder).Where("order_sn = ?", orderSn).First(preOrder).Error; err != nil {
		return nil, err
	}
	return preOrder, nil
}

// 取消主单下的止盈止损
// mainId 主单id
// symbolType 1现货 2合约
func cancelSymbolTakeAndStop(db *gorm.DB, mainId int, symbolType int) error {
	orders, err := GetSymbolTakeAndStop(db, mainId, symbolType)
	errStr := ""

	if err != nil {
		return err
	}

	for _, order := range orders {
		apiUserInfo, _ := GetApiInfo(order.ApiId)
		if apiUserInfo.Id == 0 {
			logger.Errorf("cancelSymbolTakeAndStop 查询api用户失败 apiid:%v ordersn:%s", order.ApiId, order.OrderSn)
		}

		switch {
		case order.OrderType == 1 && symbolType == 1:
			err = CancelOpenOrderByOrderSnLoop(apiUserInfo, order.Symbol, order.OrderSn)

			if err != nil {
				errStr += fmt.Sprintf("取消止盈失败,订单号:%s,错误:%v  ", order.OrderSn, err)
			}
		case order.OrderType == 2 && symbolType == 1:
			key := fmt.Sprintf(rediskey.SpotStopLossList, global.EXCHANGE_BINANCE)
			stoplosss, _ := helper.DefaultRedis.GetAllList(key)
			stop := dto.StopLossRedisList{}

			for _, item := range stoplosss {
				sonic.Unmarshal([]byte(item), &stop)

				if stop.MainId == order.MainId {
					_, err2 := helper.DefaultRedis.LRem(key, item)

					if err2 != nil {
						logger.Errorf("移除止损缓存失败 err:%v", err2)
					}
				}
			}
		case order.OrderType == 1 && symbolType == 2:
			err = CancelFutOrderByOrderSnLoop(apiUserInfo, order.Symbol, order.OrderSn)

			if err != nil {
				errStr += fmt.Sprintf("取消止盈失败,订单号:%s,错误:%v  ", order.OrderSn, err)
			}
		case order.OrderType == 2 && symbolType == 2:
			err = CancelFutOrderByOrderSnLoop(apiUserInfo, order.Symbol, order.OrderSn)

			if err != nil {
				errStr += fmt.Sprintf("取消止损失败,订单号:%s,错误:%v  ", order.OrderSn, err)
			}
		}
	}

	return nil
}

// 保存仓位信息
func savePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) positiondto.PositionDto {
	positionManage := positionservice.BinancePositionManagement{}
	positionManage.Orm = db
	positionReq := positiondto.PositionAddReq{
		ApiId:      preOrder.ApiId,
		SymbolType: preOrder.SymbolType,
		Symbol:     preOrder.Symbol,
		Price:      utility.StrToDecimal(preOrder.Price),
		// Side:       preOrder.Site,
		Quantity: utility.StrToDecimal(preOrder.Num),
	}

	switch {
	case preOrder.OrderType == 0 && preOrder.Site == "BUY",
		preOrder.OrderType != 0 && preOrder.Site == "SELL":
		positionReq.PositionSide = "LONG"
	case preOrder.OrderType == 0 && preOrder.Site == "SELL",
		preOrder.OrderType != 0 && preOrder.Site == "BUY":
		positionReq.PositionSide = "SHORT"
	}

	//减仓单 数量为负
	if preOrder.OrderType != 0 {
		if preOrder.Site == "BUY" {
			positionReq.Side = "SELL"
		} else {
			positionReq.Side = "BUY"
		}

		positionReq.Quantity = positionReq.Quantity.Mul(decimal.NewFromInt(-1))
	} else {
		positionReq.Side = preOrder.Site
	}

	positionData, err := positionManage.SavePosition(&positionReq, global.EXCHANGE_BINANCE)

	if err != nil {
		logger.Error("保存持仓信息失败, 主单号：%s, 错误信息：%v", preOrder.OrderSn, err)
	}
	return positionData
}

// 获取已开仓的主单id
func getOpenPositionMainOrderId(db *gorm.DB, newId, apiId, symbolType int, exchangeType, symbol, side string) ([]DbModels.LinePreOrder, error) {
	mainOrders := make([]DbModels.LinePreOrder, 0)

	if err := db.Model(&DbModels.LinePreOrder{}).
		Where("api_id =? AND status>4 AND order_type =0 AND status<7 AND symbol=? AND symbol_type =? AND site= ? AND exchange_type=? AND id!=?",
			apiId, symbol, symbolType, side, exchangeType, newId).
		Select("id", "main_id", "order_sn").Find(&mainOrders).Error; err != nil {
		return nil, err
	}

	return mainOrders, nil
}

// 获取需要取消的订单号
func GetOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) {
	result := []string{}

	//委托中的订单
	if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id IN ? AND status=5", mainIds).Select("order_sn").Find(&result).Error; err != nil {
		return nil, err
	}

	return result, nil
}
