package binanceservice

import (
	"database/sql"
	"errors"
	"fmt"
	DbModels "go-admin/app/admin/models"
	"go-admin/common/global"
	"go-admin/common/helper"
	"go-admin/pkg/maphelper"
	"go-admin/services/cacheservice"
	"strconv"
	"time"

	"github.com/go-admin-team/go-admin-core/sdk/service"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
)

type ReverseService struct {
	service.Service
}

// SaveMainOrder 保存订单信息
// mapData: 下单数据
// status: 订单状态
// orderSn: 自定义订单号
func (e *ReverseService) handleSimpleStatusChange(status string, orderSn string, mapData map[string]interface{}) (bool, error) {
	statusMap := map[string]int{
		"NEW":              2,
		"CANCELED":         6,
		"EXPIRED":          7,
		"EXPIRED_IN_MATCH": 7,
	}
	if newStatus, ok := statusMap[status]; ok {
		_ = e.changeOrderStatus(newStatus, orderSn, mapData)
		return false, nil
	}
	return false, fmt.Errorf("不支持的订单状态 %s", status)
}

// ReverseOrder 反向下单
// apiKey: api key
// mapData: 下单数据
// return apiInfo, bool
// bool: true=需要反单, false=不需要反单
func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interface{}) (bool, error) {
	apiInfo := GetApiInfoByKey(apiKey)

	if apiInfo.Id == 0 {
		e.Log.Errorf("获取apiInfo失败 %s", apiKey)
		return false, nil
	}

	status, _ := maphelper.GetString(mapData, "X")
	orderSn, err := maphelper.GetString(mapData, "c")
	if err != nil {
		return true, err
	}
	ot, err := maphelper.GetString(mapData, "ot")

	if err != nil {
		return true, err
	}

	switch status {
	case "NEW", "CANCELED", "EXPIRED", "EXPIRED_IN_MATCH":
		result, err := e.handleSimpleStatusChange(status, orderSn, mapData)

		//如果是 新开止盈止损 需要取消反单的止盈止损之后重下反单止盈止损
		if status == "NEW" && (ot == "TAKE_PROFIT_MARKET" || ot == "STOP_LOSS_MARKET" || ot == "TAKE_PROFIT_LIMIT" || ot == "STOP_LOSS_LIMIT") &&
			apiInfo.ReverseStatus == 1 && apiInfo.OpenStatus == 1 && apiInfo.ReverseApiId > 0 {

			if err := e.ReTakeOrStopOrder(&mapData, orderSn, &apiInfo); err != nil {
				return true, err
			}
		}

		return result, err
	case "FILLED":
		if apiInfo.ReverseStatus == 1 && apiInfo.OpenStatus == 1 && apiInfo.ReverseApiId > 0 {
			reverseApiInfo, err := GetApiInfo(apiInfo.ReverseApiId)
			if err != nil {
				e.Log.Errorf("获取反向api信息失败 reverseApiId:%d, err:%v", apiInfo.ReverseApiId, err)
				return true, err
			}

			mainOrder, err := e.SaveMainOrder(mapData, apiInfo)
			if err != nil {
				return false, err
			}

			switch {
			case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL":
				if mainOrder.Category == 0 {
					if err1 := e.savePosition(&mainOrder, reverseApiInfo.Id, true, false, false); err1 != nil {
						return true, err1
					}

					e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, false, false)
				}
			case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
				if mainOrder.Category == 0 {
					closePosition := maphelper.GetBool(mapData, "R")
					if err1 := e.savePosition(&mainOrder, reverseApiInfo.Id, true, true, closePosition); err1 != nil {
						e.Log.Errorf("保存主订单失败: %v", err1)
						return true, err1
					}
					e.DoAddReverseOrder(&mainOrder, &reverseApiInfo, apiInfo.OrderProportion, true, closePosition)
				}
			default:
				return true, errors.New("不支持的订单类型")
			}
			return true, nil
		} else if apiInfo.Subordinate == "2" {
			symbol, err := maphelper.GetString(mapData, "s")

			if err != nil {
				return true, err
			}
			positionSide, err := maphelper.GetString(mapData, "ps")

			if err != nil {
				return true, err
			}
			side, err := maphelper.GetString(mapData, "S")
			if err != nil {
				return true, err
			}

			totalNum := maphelper.GetDecimal(mapData, "z")

			mainOrder := DbModels.LineReverseOrder{
				ApiId:        apiInfo.Id,
				Symbol:       symbol,
				PositionSide: positionSide,
				TotalNum:     totalNum,
				Side:         side,
			}
			e.changeOrderStatus(3, orderSn, mapData)

			switch {
			case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL":
				if mainOrder.Category == 0 {
					if err1 := e.savePosition(&mainOrder, 0, false, false, false); err1 != nil {
						return true, err1
					}
				}
			case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
				if mainOrder.Category == 0 {
					closePosition := maphelper.GetBool(mapData, "R")
					if err1 := e.savePosition(&mainOrder, 0, false, true, closePosition); err1 != nil {
						return true, err1
					}
				}
			default:
				return true, errors.New("不支持的订单类型")
			}
		}
	default:
		return false, fmt.Errorf("不支持的订单状态 %s", status)
	}

	return false, nil
}

// 修改订单状态
// status: 订单状态 1-待下单 2-已下单 3-已成交 6-已取消 7-已过期
func (e *ReverseService) changeOrderStatus(status int, orderSn string, mapData map[string]interface{}) error {
	data := map[string]interface{}{"status": status, "updated_at": time.Now()}

	if status == 3 {
		now := time.Now()
		if orderId, ok := mapData["i"].(float64); ok {
			data["order_id"] = orderId
		}

		if ap, ok := mapData["ap"].(bool); ok {
			data["final_price"] = ap
		}

		if num, ok := mapData["z"].(string); ok {
			data["total_num"], _ = decimal.NewFromString(num)
		}

		data["trigger_time"] = &now
	}

	db := e.Orm.Model(&DbModels.LineReverseOrder{}).
		Where("order_sn =? and status != 3", orderSn).
		Updates(data)

	if db.Error != nil {
		e.Log.Errorf("修改订单状态失败 orderSn:%s, err:%v", orderSn, db.Error)
	}

	if db.RowsAffected == 0 {
		e.Log.Errorf("修改订单状态失败 orderSn:%s, 未找到订单", orderSn)
	}
	return db.Error
}

// 先保存主单,持仓信息 必须用双向持仓!
func (e *ReverseService) SaveMainOrder(mapData map[string]interface{}, apiInfo DbModels.LineApiUser) (DbModels.LineReverseOrder, error) {
	now := time.Now()
	symbol, err := maphelper.GetString(mapData, "s")
	var reverseOrder DbModels.LineReverseOrder
	if err != nil {
		return reverseOrder, err
	}

	orderSn, err := maphelper.GetString(mapData, "c")
	if err != nil {
		return reverseOrder, err
	}

	side, err := maphelper.GetString(mapData, "S")
	if err != nil {
		return reverseOrder, err
	}

	positionSide, err := maphelper.GetString(mapData, "ps")
	if err != nil {
		return reverseOrder, err
	}

	mainType, _ := maphelper.GetString(mapData, "ot")

	reverseOrder = DbModels.LineReverseOrder{
		ApiId:         apiInfo.Id,
		Category:      0,
		OrderSn:       orderSn,
		TriggerTime:   &now,
		Status:        3,
		Symbol:        symbol,
		FollowOrderSn: "",
		Type:          mainType,
		Price:         maphelper.GetDecimal(mapData, "p"),
		FinalPrice:    maphelper.GetDecimal(mapData, "ap"),
		TotalNum:      maphelper.GetDecimal(mapData, "z"),
		Side:          side,
		PositionSide:  positionSide,
	}

	reverseOrder.PriceU = reverseOrder.Price

	if !reverseOrder.Price.IsZero() && !reverseOrder.TotalNum.IsZero() {
		reverseOrder.BuyPrice = reverseOrder.Price.Mul(reverseOrder.TotalNum)
	}

	if id, err := maphelper.GetFloat64(mapData, "i"); err == nil {
		reverseOrder.OrderId = strconv.FormatFloat(id, 'f', -1, 64)
	}

	orderType, _ := maphelper.GetString(mapData, "ot")
	switch orderType {
	case "LIMIT", "MARKET":
		reverseOrder.OrderType = 0
	case "TAKE_PROFIT_MARKET", "TAKE_PROFIT":
		reverseOrder.OrderType = 1
	case "STOP_MARKET", "STOP", "TRAILING_STOP_MARKET":
		reverseOrder.OrderType = 2
	default:
		return reverseOrder, fmt.Errorf("不支持的订单类型: %s", orderType)
	}

	if reverseOrder.PositionSide == "BOTH" {
		return reverseOrder, errors.New("不支持的持仓类型,必须为双向持仓")
	}

	if err := e.Orm.Create(&reverseOrder).Error; err != nil {
		e.Log.Errorf("保存主单失败:%v", err)
		return reverseOrder, err
	}

	return reverseOrder, nil
}

// 更新仓位信息
// closePosition: true=平仓, false=减仓
func (e *ReverseService) savePosition(reverseOrder *DbModels.LineReverseOrder, reverseApiId int, isMain, reducePosition, closePosition bool) error {
	position := DbModels.LineReversePosition{}
	positionSide := reverseOrder.PositionSide
	side := reverseOrder.Side
	totalNum := reverseOrder.TotalNum

	var querySql string
	sqlStr := ""

	//如果是主单,存储仓位则是反单的持仓方向
	if isMain {
		if reverseOrder.PositionSide == "LONG" {
			positionSide = "SHORT"
		} else {
			positionSide = "LONG"
		}

		if !reducePosition {
			//反单止盈止损方向相反
			if side == "SELL" {
				side = "BUY"
			} else {
				side = "SELL"
			}

			position.ReverseApiId = reverseApiId
			position.Side = side
			position.ApiId = reverseOrder.ApiId
			position.Symbol = reverseOrder.Symbol
			position.Status = 1
			position.ReverseStatus = 0
			position.PositionSide = positionSide
		}
		querySql = "api_id =? and position_side =? and symbol =? and status =1"

		//平仓
		if closePosition {
			totalNum = decimal.Zero
			sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2 where id =@id and status!=2 "
		} else if reducePosition {
			//只减仓
			sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now() where id =@id and status!=2 "
		} else {
			sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now() where id =@id and status!=2"
		}
	} else {
		querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)"

		if closePosition {
			totalNum = decimal.Zero
			sqlStr = "UPDATE line_reverse_position set reverse_amount=@totalNum,updated_at=now(),reverse_status=2 where id =@id and reverse_status !=2"
		} else if reducePosition {
			sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now() where id =@id and reverse_status !=2"
		} else {
			sqlStr = "UPDATE line_reverse_position set total_reverse_amount=total_reverse_amount + @totalNum,reverse_amount=reverse_amount + @totalNum,updated_at=now(),reverse_status =1 where id =@id and reverse_status !=2"
		}
	}

	err := e.Orm.Transaction(func(tx *gorm.DB) error {
		err1 := tx.Model(&position).Where(querySql,
			reverseOrder.ApiId, positionSide, reverseOrder.Symbol).First(&position).Error

		if err1 != nil {
			//主单仓位不存在,创建新仓位
			if isMain && errors.Is(err1, gorm.ErrRecordNotFound) && !reducePosition {

				if err2 := tx.Create(&position).Error; err2 != nil {
					return err2
				}
			} else {
				return err1
			}
		}

		dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id))
		if dbResult.Error != nil {
			return dbResult.Error
		}

		if dbResult.RowsAffected == 0 {
			e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, reverseOrder)
			return errors.New("没有找到对应的持仓信息")
		}

		return nil
	})

	return err
}

// 反向下单
// mainOrder: 主单信息
// reverseApiId: 反向apiId
// orderProportion: 反向下单比例
// reducePosition: 是否减仓
// closePosition: 是否平仓
func (e *ReverseService) DoAddReverseOrder(mainOrder *DbModels.LineReverseOrder, reverseApiInfo *DbModels.LineApiUser, orderProportion decimal.Decimal, reducePosition, closePosition bool) error {
	order := DbModels.LineReverseOrder{}
	order.ApiId = reverseApiInfo.Id
	order.Category = 1
	order.OrderSn = helper.GetOrderNo()
	order.FollowOrderSn = mainOrder.OrderSn
	order.Symbol = mainOrder.Symbol
	order.OrderType = 0

	switch mainOrder.PositionSide {
	case "LONG":
		order.PositionSide = "SHORT"
	case "SHORT":
		order.PositionSide = "LONG"
	}

	switch mainOrder.Side {
	case "SELL":
		order.Side = "BUY"
	case "BUY":
		order.Side = "SELL"
	default:
		return fmt.Errorf("不支持的订单类型 side:%s", mainOrder.Side)
	}

	if reducePosition && closePosition {
		order.OrderType = 4
	} else if reducePosition {
		order.OrderType = 3
	}

	symbol, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, mainOrder.Symbol, 1)

	if err != nil {
		e.Log.Errorf("获取交易对信息失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
		return err
	}

	setting, err := cacheservice.GetReverseSetting(e.Orm)

	if err != nil {
		e.Log.Errorf("获取反单设置失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
		return err
	}

	signPrice, _ := decimal.NewFromString(symbol.LastPrice)
	price := signPrice.Truncate(int32(symbol.PriceDigit))

	if price.Cmp(decimal.Zero) <= 0 {
		e.Log.Errorf("获取最新价格失败 symbol:%s custom:%s 单价小于0", mainOrder.Symbol, mainOrder.OrderSn)
		return errors.New("获取最新价格失败")
	}

	var percent decimal.Decimal
	switch {
	case order.PositionSide == "LONG" && order.Side == "BUY", order.PositionSide == "SHORT" && order.Side == "BUY":
		percent = decimal.NewFromInt(100).Add(setting.ReversePremiumRatio)
	case order.PositionSide == "SHORT" && order.Side == "SELL", order.PositionSide == "LONG" && order.Side == "SELL":
		percent = decimal.NewFromInt(100).Sub(setting.ReversePremiumRatio)
	default:
		return fmt.Errorf("不支持的订单类型 ps:%s, side:%s", order.PositionSide, order.Side)
	}

	percent = percent.Div(decimal.NewFromInt(100)).Truncate(4)
	//计算溢价单价
	price = price.Mul(percent).Truncate(int32(symbol.PriceDigit))
	var amount decimal.Decimal

	//平仓单直接卖出全部数量
	if closePosition {
		var position DbModels.LineReversePosition
		if err1 := e.Orm.Model(position).
			Where("position_side =? and reverse_api_id =? and  reverse_status =1", order.PositionSide, order.ApiId).
			Select("reverse_amount").
			Find(&position).Error; err1 != nil {
			e.Log.Errorf("获取剩余仓位失败 symbol:%s custom:%s :%v", order.Symbol, order.OrderSn, err1)

			if len(err1.Error()) < 255 {
				order.Remark = err1.Error()
			} else {
				order.Remark = err1.Error()[:254]
			}
		}

		amount = position.ReverseAmount

		if amount.IsZero() {
			order.Remark = "没有持仓数量"
		}
	} else {
		proportion := decimal.NewFromInt(100)

		if orderProportion.Cmp(decimal.Zero) > 0 {
			proportion = orderProportion
		}

		//反向下单百分比
		proportion = proportion.Div(decimal.NewFromInt(100)).Truncate(2)
		amount = mainOrder.BuyPrice.Mul(proportion).Div(price).Truncate(int32(symbol.AmountDigit))

		if amount.Cmp(decimal.Zero) <= 0 {
			e.Log.Errorf("计算数量失败 symbol:%s custom:%s 数量小于0", mainOrder.Symbol, mainOrder.OrderSn)
			return errors.New("计算数量失败")
		}
	}

	order.TotalNum = amount
	order.Price = price
	order.PriceU = price
	order.BuyPrice = amount.Mul(price).Truncate(int32(symbol.PriceDigit))
	order.Status = 1
	order.Type = setting.ReverseOrderType
	order.SignPrice = signPrice

	if order.Remark != "" {
		order.Status = 8
	}

	if err := e.Orm.Model(&order).Create(&order).Error; err != nil {
		e.Log.Errorf("保存反单失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
		return err
	}

	if order.Status == 1 {
		e.DoBianceOrder(&order, reverseApiInfo, &setting, reducePosition, closePosition)
	}

	return nil
}

// 处理币安订单
// order: 反单信息
// apiInfo: 币安api信息
func (e *ReverseService) DoBianceOrder(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, setting *DbModels.LineReverseSetting, reducePosition, closePosition bool) error {
	futApiV2 := FuturesResetV2{Service: e.Service}
	orderType := setting.ReverseOrderType

	if orderType == "" {
		orderType = "LIMIT"
	}

	params := FutOrderPlace{
		ApiId:            apiInfo.Id,
		Symbol:           order.Symbol,
		PositionSide:     order.PositionSide,
		Side:             order.Side,
		OrderType:        orderType,
		Quantity:         order.TotalNum,
		Price:            order.Price,
		NewClientOrderId: order.OrderSn,
	}

	err := futApiV2.OrderPlaceLoop(apiInfo, params)

	if err != nil {
		e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", order.Symbol, order.OrderSn, err)
		remark := err.Error()

		if len(remark) > 255 {
			remark = remark[:254]
		}

		if err1 := e.Orm.Model(&order).Where("id =? and status !=3", order.Id).Updates(map[string]interface{}{"status": 8, "remark": remark, "updated_at": time.Now()}).Error; err1 != nil {
			e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", err1)
		}
	}

	return nil
}

// 重下止盈止损
// mapData: 主单止盈止损回调
func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser) error {
	symbol, err := maphelper.GetString(*mapData, "s")

	if err != nil {
		return err
	}

	side, err := maphelper.GetString(*mapData, "S")

	if err != nil {
		return err
	}

	ot, err := maphelper.GetString(*mapData, "ot")

	if err != nil {
		return err
	}

	//反单止盈止损方向相反
	if side == "SELL" {
		side = "BUY"
	} else {
		side = "SELL"
	}

	positionSide, err := maphelper.GetString(*mapData, "ps")

	if err != nil {
		return err
	}

	if positionSide == "LONG" {
		positionSide = "SHORT"
	} else {
		positionSide = "LONG"
	}

	apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId)

	if err != nil {
		e.Log.Errorf("根据主单api获取反单api失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
		return err
	}

	var reverseOrder DbModels.LineReverseOrder
	e.Orm.Model(&DbModels.LineReverseOrder{}).
		Where("symbol =? and api_id =? and position_side =? and side = ? and status =1", symbol, mainApiInfo.ReverseApiId, positionSide, side).
		First(&reverseOrder)

	//取消旧止盈止损
	if reverseOrder.OrderSn != "" {
		futApi := FutRestApi{}
		err := futApi.CancelFutOrderRetry(apiInfo, symbol, reverseOrder.OrderSn)

		if err != nil {
			e.Log.Errorf("币安撤单失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
			return err
		}
	}

	params := FutOrderPlace{
		ApiId:            apiInfo.Id,
		Symbol:           symbol,
		PositionSide:     positionSide,
		Side:             side,
		OrderType:        ot,
		Quantity:         reverseOrder.TotalNum,
		Price:            reverseOrder.Price,
		NewClientOrderId: reverseOrder.OrderSn,
	}
	futApiV2 := FuturesResetV2{Service: e.Service}
	futApiV2.OrderPlace(&apiInfo, params)
	return nil
}
