package positionservice

import (
	"context"
	"fmt"
	"go-admin/common/const/rediskey"
	"go-admin/common/helper"
	"go-admin/common/service"
	"go-admin/models/positiondto"
	"time"

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

type BinancePositionManagement struct {
	service.Service
}

// 保存仓位信息
// return 仓位信息
// return 错误信息
func (e *BinancePositionManagement) SavePosition(data *positiondto.PositionAddReq, exchangeType string) (positiondto.PositionDto, error) {
	var key string
	result := positiondto.PositionDto{}

	switch data.SymbolType {
	case 1:
		key = fmt.Sprintf(rediskey.SpotPosition, exchangeType, data.ApiId, data.Symbol, data.Side)
	case 2:
		key = fmt.Sprintf(rediskey.FuturePosition, exchangeType, data.ApiId, data.Symbol, data.Side)
	default:
		return result, fmt.Errorf("symbol type error")
	}

	lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotPositionLock, data.ApiId, data.Symbol, data.Side), 200, 5, 200*time.Millisecond)

	if ok, err := lock.AcquireWait(context.Background()); err != nil {
		logger.Debug("获取锁失败", err)
		return result, err
	} else if ok {
		defer lock.Release()
		val, _ := helper.DefaultRedis.GetString(key)

		if val != "" {
			sonic.Unmarshal([]byte(val), &result)
		}

		if result.Symbol == "" {
			result.Symbol = data.Symbol
			result.Side = data.Side
			result.ApiId = data.ApiId
			result.SymbolType = data.SymbolType
			result.PositionSide = data.PositionSide
		}

		var totalLoss decimal.Decimal
		if result.LastPrice.Cmp(decimal.Zero) > 0 {
			switch {
			//多 买入
			case data.PositionSide == "LONG":
				totalLoss = result.LastPrice.Sub(data.Price).Mul(result.Quantity)

				if data.Price.Cmp(result.LastPrice) < 0 {
					result.LastPrice = data.Price
				}
			case data.PositionSide == "SHORT":
				totalLoss = data.Price.Sub(result.LastPrice).Mul(result.Quantity)

				if data.Price.Cmp(result.LastPrice) > 0 {
					result.LastPrice = data.Price
				}
			}
		} else {
			//默认没有金额的时候
			result.LastPrice = data.Price
		}

		//总亏损大于0
		if totalLoss.Add(result.TotalLoss).Cmp(decimal.Zero) > 0 {
			result.TotalLoss = result.TotalLoss.Add(totalLoss)
		} else {
			result.TotalLoss = decimal.Zero
		}

		result.Quantity = data.Quantity.Add(result.Quantity)

		dataVal, _ := sonic.MarshalString(result)
		if err := helper.DefaultRedis.SetString(key, dataVal); err != nil {
			logger.Errorf("保存仓位信息失败,val:%s err:%v", dataVal, err)
		}
	}

	return result, nil
}

// 获取系统内仓位信息
func (e *BinancePositionManagement) GetPosition(apiId, symbolType int, exchangeTyp, symbol, side string) (positiondto.PositionDto, error) {
	result := positiondto.PositionDto{}
	var key string

	switch symbolType {
	case 1:
		key = fmt.Sprintf(rediskey.SpotPosition, exchangeTyp, apiId, symbol, side)
	case 2:
		key = fmt.Sprintf(rediskey.FuturePosition, exchangeTyp, apiId, symbol, side)
	default:
		return result, fmt.Errorf("symbol type error")
	}

	val, _ := helper.DefaultRedis.GetString(key)
	if val != "" {
		sonic.Unmarshal([]byte(val), &result)
	}

	return result, nil
}

// 移除仓位信息
func (e *BinancePositionManagement) RemovePosition(req *positiondto.LinePreOrderPositioinDelReq) error {
	var key string

	switch req.SymbolType {
	case 1:
		key = fmt.Sprintf(rediskey.SpotPosition, req.ExchangeType, req.ApiId, req.Symbol, req.Side)
	case 2:
		key = fmt.Sprintf(rediskey.FuturePosition, req.ExchangeType, req.ApiId, req.Symbol, req.Side)
	default:
		return fmt.Errorf("symbol type error")
	}

	return helper.DefaultRedis.DeleteString(key)
}
