package gate

import (
	"fmt"
	"math"
	"nofx/logger"
	"nofx/trader/types"
	"strconv"
	"strings"

	"github.com/antihax/optional"
	"github.com/gateio/gateapi-go/v6"
)

// SetLeverage sets the leverage for a symbol
func (t *GateTrader) SetLeverage(symbol string, leverage int) error {
	symbol = t.convertSymbol(symbol)

	_, _, err := t.client.FuturesApi.UpdatePositionLeverage(t.ctx, "usdt", symbol, fmt.Sprintf("%d", leverage), nil)
	if err != nil {
		// Gate.io may return error if leverage is already set
		if strings.Contains(err.Error(), "RISK_LIMIT_EXCEEDED") {
			logger.Warnf("  [Gate] Leverage %d exceeds limit for %s", leverage, symbol)
			return nil
		}
		return fmt.Errorf("failed to set leverage: %w", err)
	}

	logger.Infof("  [Gate] Leverage set to %dx for %s", leverage, symbol)
	return nil
}

// SetMarginMode sets margin mode (cross or isolated)
func (t *GateTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
	// Gate.io uses leverage=0 for cross margin, positive number for isolated
	// This is handled through UpdatePositionLeverage with cross_leverage_limit
	// For now, we'll skip explicit margin mode setting as it's tied to leverage
	logger.Infof("  [Gate] Margin mode is set through leverage (0=cross)")
	return nil
}

// OpenLong opens a long position
func (t *GateTrader) OpenLong(symbol string, quantity float64, leverage int) (map[string]interface{}, error) {
	symbol = t.convertSymbol(symbol)

	// Cancel old orders first
	if err := t.CancelAllOrders(symbol); err != nil {
		logger.Infof("  ⚠ Failed to cancel old pending orders (may not have any): %v", err)
	}

	// Set leverage
	if err := t.SetLeverage(symbol, leverage); err != nil {
		logger.Warnf("  [Gate] Failed to set leverage: %v", err)
	}

	// Get contract info for size calculation
	contract, err := t.getContract(symbol)
	if err != nil {
		return nil, err
	}

	// Gate uses contract size units (each contract = quanto_multiplier base currency)
	// size = quantity / quanto_multiplier
	quantoMultiplier, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
	size := int64(quantity / quantoMultiplier)
	if size <= 0 {
		size = 1
	}

	order := gateapi.FuturesOrder{
		Contract: symbol,
		Size:     size, // Positive for long
		Price:    "0",  // Market order
		Tif:      "ioc",
		Text:     "t-nofx",
	}

	logger.Infof("  [Gate] OpenLong: symbol=%s, size=%d, leverage=%d", symbol, size, leverage)

	result, _, err := t.client.FuturesApi.CreateFuturesOrder(t.ctx, "usdt", order, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to open long position: %w", err)
	}

	// Clear cache
	t.clearCache()

	// Parse fill price from result
	fillPrice, _ := strconv.ParseFloat(result.FillPrice, 64)

	logger.Infof("  [Gate] Opened long position: orderId=%d, fillPrice=%.4f", result.Id, fillPrice)

	return map[string]interface{}{
		"orderId":   fmt.Sprintf("%d", result.Id),
		"symbol":    t.revertSymbol(symbol),
		"status":    "FILLED",
		"fillPrice": fillPrice,
		"avgPrice":  fillPrice,
	}, nil
}

// OpenShort opens a short position
func (t *GateTrader) OpenShort(symbol string, quantity float64, leverage int) (map[string]interface{}, error) {
	symbol = t.convertSymbol(symbol)

	// Cancel old orders first
	if err := t.CancelAllOrders(symbol); err != nil {
		logger.Infof("  ⚠ Failed to cancel old pending orders (may not have any): %v", err)
	}

	// Set leverage
	if err := t.SetLeverage(symbol, leverage); err != nil {
		logger.Warnf("  [Gate] Failed to set leverage: %v", err)
	}

	// Get contract info for size calculation
	contract, err := t.getContract(symbol)
	if err != nil {
		return nil, err
	}

	// Gate uses contract size units
	quantoMultiplier, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
	size := int64(quantity / quantoMultiplier)
	if size <= 0 {
		size = 1
	}

	order := gateapi.FuturesOrder{
		Contract: symbol,
		Size:     -size, // Negative for short
		Price:    "0",   // Market order
		Tif:      "ioc",
		Text:     "t-nofx",
	}

	logger.Infof("  [Gate] OpenShort: symbol=%s, size=%d, leverage=%d", symbol, -size, leverage)

	result, _, err := t.client.FuturesApi.CreateFuturesOrder(t.ctx, "usdt", order, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to open short position: %w", err)
	}

	// Clear cache
	t.clearCache()

	// Parse fill price from result
	fillPrice, _ := strconv.ParseFloat(result.FillPrice, 64)

	logger.Infof("  [Gate] Opened short position: orderId=%d, fillPrice=%.4f", result.Id, fillPrice)

	return map[string]interface{}{
		"orderId":   fmt.Sprintf("%d", result.Id),
		"symbol":    t.revertSymbol(symbol),
		"status":    "FILLED",
		"fillPrice": fillPrice,
		"avgPrice":  fillPrice,
	}, nil
}

// CloseLong closes a long position
func (t *GateTrader) CloseLong(symbol string, quantity float64) (map[string]interface{}, error) {
	symbol = t.convertSymbol(symbol)

	// If quantity is 0, get current position
	if quantity == 0 {
		positions, err := t.GetPositions()
		if err != nil {
			return nil, err
		}
		for _, pos := range positions {
			posSymbol := t.convertSymbol(pos["symbol"].(string))
			if posSymbol == symbol && pos["side"] == "long" {
				quantity = pos["positionAmt"].(float64)
				break
			}
		}
		if quantity == 0 {
			return nil, fmt.Errorf("long position not found for %s", symbol)
		}
	}

	// Get contract info for size calculation
	contract, err := t.getContract(symbol)
	if err != nil {
		return nil, err
	}

	quantoMultiplier, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
	size := int64(quantity / quantoMultiplier)
	if size <= 0 {
		size = 1
	}

	// Close long = sell (use ReduceOnly, not Close which requires Size=0)
	order := gateapi.FuturesOrder{
		Contract:   symbol,
		Size:       -size, // Negative to close long
		Price:      "0",
		Tif:        "ioc",
		ReduceOnly: true,
		Text:       "t-nofx-close",
	}

	logger.Infof("  [Gate] CloseLong: symbol=%s, size=%d", symbol, -size)

	result, _, err := t.client.FuturesApi.CreateFuturesOrder(t.ctx, "usdt", order, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to close long position: %w", err)
	}

	// Clear cache
	t.clearCache()

	// Parse fill price from result
	fillPrice, _ := strconv.ParseFloat(result.FillPrice, 64)

	logger.Infof("  [Gate] Closed long position: orderId=%d, fillPrice=%.4f", result.Id, fillPrice)

	return map[string]interface{}{
		"orderId":   fmt.Sprintf("%d", result.Id),
		"symbol":    t.revertSymbol(symbol),
		"status":    "FILLED",
		"fillPrice": fillPrice,
		"avgPrice":  fillPrice,
	}, nil
}

// CloseShort closes a short position
func (t *GateTrader) CloseShort(symbol string, quantity float64) (map[string]interface{}, error) {
	symbol = t.convertSymbol(symbol)

	// If quantity is 0, get current position
	if quantity == 0 {
		positions, err := t.GetPositions()
		if err != nil {
			return nil, err
		}
		for _, pos := range positions {
			posSymbol := t.convertSymbol(pos["symbol"].(string))
			if posSymbol == symbol && pos["side"] == "short" {
				quantity = pos["positionAmt"].(float64)
				break
			}
		}
		if quantity == 0 {
			return nil, fmt.Errorf("short position not found for %s", symbol)
		}
	}

	// Ensure quantity is positive
	if quantity < 0 {
		quantity = -quantity
	}

	// Get contract info for size calculation
	contract, err := t.getContract(symbol)
	if err != nil {
		return nil, err
	}

	quantoMultiplier, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
	size := int64(quantity / quantoMultiplier)
	if size <= 0 {
		size = 1
	}

	// Close short = buy (use ReduceOnly, not Close which requires Size=0)
	order := gateapi.FuturesOrder{
		Contract:   symbol,
		Size:       size, // Positive to close short
		Price:      "0",
		Tif:        "ioc",
		ReduceOnly: true,
		Text:       "t-nofx-close",
	}

	logger.Infof("  [Gate] CloseShort: symbol=%s, size=%d", symbol, size)

	result, _, err := t.client.FuturesApi.CreateFuturesOrder(t.ctx, "usdt", order, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to close short position: %w", err)
	}

	// Clear cache
	t.clearCache()

	// Parse fill price from result
	fillPrice, _ := strconv.ParseFloat(result.FillPrice, 64)

	logger.Infof("  [Gate] Closed short position: orderId=%d, fillPrice=%.4f", result.Id, fillPrice)

	return map[string]interface{}{
		"orderId":   fmt.Sprintf("%d", result.Id),
		"symbol":    t.revertSymbol(symbol),
		"status":    "FILLED",
		"fillPrice": fillPrice,
		"avgPrice":  fillPrice,
	}, nil
}

// GetMarketPrice gets the current market price
func (t *GateTrader) GetMarketPrice(symbol string) (float64, error) {
	symbol = t.convertSymbol(symbol)

	opts := &gateapi.ListFuturesTickersOpts{
		Contract: optional.NewString(symbol),
	}

	tickers, _, err := t.client.FuturesApi.ListFuturesTickers(t.ctx, "usdt", opts)
	if err != nil {
		return 0, fmt.Errorf("failed to get market price: %w", err)
	}

	if len(tickers) == 0 {
		return 0, fmt.Errorf("no ticker data for %s", symbol)
	}

	price, _ := strconv.ParseFloat(tickers[0].Last, 64)
	return price, nil
}

// SetStopLoss sets a stop loss order
func (t *GateTrader) SetStopLoss(symbol string, positionSide string, quantity, stopPrice float64) error {
	symbol = t.convertSymbol(symbol)

	contract, err := t.getContract(symbol)
	if err != nil {
		return err
	}

	quantoMultiplier, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
	size := int64(quantity / quantoMultiplier)
	if size <= 0 {
		size = 1
	}

	// For long position, stop loss means sell when price drops
	// For short position, stop loss means buy when price rises
	if strings.ToUpper(positionSide) == "LONG" {
		size = -size
	}

	// Use price trigger order
	trigger := gateapi.FuturesPriceTriggeredOrder{
		Initial: gateapi.FuturesInitialOrder{
			Contract:   symbol,
			Size:       size,
			Price:      "0", // Market order
			Tif:        "ioc",
			ReduceOnly: true,
			Close:      true,
		},
		Trigger: gateapi.FuturesPriceTrigger{
			StrategyType: 0, // Close position
			PriceType:    0, // Latest price
			Price:        fmt.Sprintf("%.8f", stopPrice),
			Rule:         1, // Price <= trigger price
		},
	}

	if strings.ToUpper(positionSide) == "SHORT" {
		trigger.Trigger.Rule = 2 // Price >= trigger price for short stop loss
	}

	_, _, err = t.client.FuturesApi.CreatePriceTriggeredOrder(t.ctx, "usdt", trigger)
	if err != nil {
		return fmt.Errorf("failed to set stop loss: %w", err)
	}

	logger.Infof("  [Gate] Stop loss set: %s @ %.4f", symbol, stopPrice)
	return nil
}

// SetTakeProfit sets a take profit order
func (t *GateTrader) SetTakeProfit(symbol string, positionSide string, quantity, takeProfitPrice float64) error {
	symbol = t.convertSymbol(symbol)

	contract, err := t.getContract(symbol)
	if err != nil {
		return err
	}

	quantoMultiplier, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
	size := int64(quantity / quantoMultiplier)
	if size <= 0 {
		size = 1
	}

	// For long position, take profit means sell when price rises
	// For short position, take profit means buy when price drops
	if strings.ToUpper(positionSide) == "LONG" {
		size = -size
	}

	trigger := gateapi.FuturesPriceTriggeredOrder{
		Initial: gateapi.FuturesInitialOrder{
			Contract:   symbol,
			Size:       size,
			Price:      "0", // Market order
			Tif:        "ioc",
			ReduceOnly: true,
			Close:      true,
		},
		Trigger: gateapi.FuturesPriceTrigger{
			StrategyType: 0, // Close position
			PriceType:    0, // Latest price
			Price:        fmt.Sprintf("%.8f", takeProfitPrice),
			Rule:         2, // Price >= trigger price for long take profit
		},
	}

	if strings.ToUpper(positionSide) == "SHORT" {
		trigger.Trigger.Rule = 1 // Price <= trigger price for short take profit
	}

	_, _, err = t.client.FuturesApi.CreatePriceTriggeredOrder(t.ctx, "usdt", trigger)
	if err != nil {
		return fmt.Errorf("failed to set take profit: %w", err)
	}

	logger.Infof("  [Gate] Take profit set: %s @ %.4f", symbol, takeProfitPrice)
	return nil
}

// CancelStopLossOrders cancels stop loss orders
func (t *GateTrader) CancelStopLossOrders(symbol string) error {
	return t.cancelTriggerOrders(symbol, "stop_loss")
}

// CancelTakeProfitOrders cancels take profit orders
func (t *GateTrader) CancelTakeProfitOrders(symbol string) error {
	return t.cancelTriggerOrders(symbol, "take_profit")
}

// cancelTriggerOrders cancels trigger orders of a specific type
func (t *GateTrader) cancelTriggerOrders(symbol string, orderType string) error {
	symbol = t.convertSymbol(symbol)

	opts := &gateapi.ListPriceTriggeredOrdersOpts{
		Contract: optional.NewString(symbol),
	}

	orders, _, err := t.client.FuturesApi.ListPriceTriggeredOrders(t.ctx, "usdt", "open", opts)
	if err != nil {
		return err
	}

	for _, order := range orders {
		// Determine if it's stop loss or take profit based on trigger rule and position
		// For simplicity, cancel all matching symbol orders
		_, _, err := t.client.FuturesApi.CancelPriceTriggeredOrder(t.ctx, "usdt", fmt.Sprintf("%d", order.Id))
		if err != nil {
			logger.Warnf("  [Gate] Failed to cancel trigger order %d: %v", order.Id, err)
		}
	}

	return nil
}

// CancelAllOrders cancels all pending orders for a symbol
func (t *GateTrader) CancelAllOrders(symbol string) error {
	symbol = t.convertSymbol(symbol)

	// Cancel regular orders
	_, _, err := t.client.FuturesApi.CancelFuturesOrders(t.ctx, "usdt", symbol, nil)
	if err != nil {
		// Ignore if no orders to cancel
		if !strings.Contains(err.Error(), "ORDER_NOT_FOUND") {
			logger.Warnf("  [Gate] Error canceling orders: %v", err)
		}
	}

	// Cancel trigger orders
	t.cancelTriggerOrders(symbol, "")

	return nil
}

// CancelStopOrders cancels all stop orders (stop loss and take profit)
func (t *GateTrader) CancelStopOrders(symbol string) error {
	t.CancelStopLossOrders(symbol)
	t.CancelTakeProfitOrders(symbol)
	return nil
}

// FormatQuantity formats quantity to correct precision
func (t *GateTrader) FormatQuantity(symbol string, quantity float64) (string, error) {
	contract, err := t.getContract(symbol)
	if err != nil {
		return fmt.Sprintf("%.4f", quantity), nil
	}

	// Gate uses quanto_multiplier for contract size
	quantoMultiplier, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
	if quantoMultiplier > 0 {
		// Calculate number of contracts
		numContracts := quantity / quantoMultiplier
		return fmt.Sprintf("%.0f", math.Floor(numContracts)), nil
	}

	return fmt.Sprintf("%.4f", quantity), nil
}

// GetOrderStatus gets the status of an order
func (t *GateTrader) GetOrderStatus(symbol string, orderID string) (map[string]interface{}, error) {
	symbol = t.convertSymbol(symbol)

	order, _, err := t.client.FuturesApi.GetFuturesOrder(t.ctx, "usdt", orderID)
	if err != nil {
		return nil, fmt.Errorf("failed to get order status: %w", err)
	}

	fillPrice, _ := strconv.ParseFloat(order.FillPrice, 64)
	tkFee, _ := strconv.ParseFloat(order.Tkfr, 64)
	mkFee, _ := strconv.ParseFloat(order.Mkfr, 64)
	totalFee := tkFee + mkFee

	// Get quanto_multiplier to convert contracts to actual quantity
	quantoMultiplier := 1.0
	contract, contractErr := t.getContract(symbol)
	if contractErr == nil && contract != nil {
		qm, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
		if qm > 0 {
			quantoMultiplier = qm
		}
	}

	// Map status
	status := "NEW"
	switch order.Status {
	case "finished":
		if order.FinishAs == "filled" {
			status = "FILLED"
		} else if order.FinishAs == "cancelled" {
			status = "CANCELED"
		} else {
			status = "CLOSED"
		}
	case "open":
		status = "NEW"
	}

	side := "BUY"
	if order.Size < 0 {
		side = "SELL"
	}

	// Convert contract count to actual token quantity
	executedQty := math.Abs(float64(order.Size-order.Left)) * quantoMultiplier

	return map[string]interface{}{
		"orderId":     orderID,
		"symbol":      t.revertSymbol(symbol),
		"status":      status,
		"avgPrice":    fillPrice,
		"executedQty": executedQty,
		"side":        side,
		"type":        order.Tif,
		"time":        int64(order.CreateTime * 1000),
		"updateTime":  int64(order.FinishTime * 1000),
		"commission":  totalFee,
	}, nil
}

// GetOpenOrders gets open/pending orders
func (t *GateTrader) GetOpenOrders(symbol string) ([]types.OpenOrder, error) {
	symbol = t.convertSymbol(symbol)

	opts := &gateapi.ListFuturesOrdersOpts{
		Contract: optional.NewString(symbol),
	}

	orders, _, err := t.client.FuturesApi.ListFuturesOrders(t.ctx, "usdt", "open", opts)
	if err != nil {
		return nil, fmt.Errorf("failed to get open orders: %w", err)
	}

	// Get quanto_multiplier to convert contracts to actual quantity
	quantoMultiplier := 1.0
	contract, err := t.getContract(symbol)
	if err == nil && contract != nil {
		qm, _ := strconv.ParseFloat(contract.QuantoMultiplier, 64)
		if qm > 0 {
			quantoMultiplier = qm
		}
	}

	var result []types.OpenOrder
	for _, order := range orders {
		price, _ := strconv.ParseFloat(order.Price, 64)

		side := "BUY"
		if order.Size < 0 {
			side = "SELL"
		}

		// Convert contract count to actual token quantity
		quantity := math.Abs(float64(order.Size)) * quantoMultiplier

		result = append(result, types.OpenOrder{
			OrderID:  fmt.Sprintf("%d", order.Id),
			Symbol:   t.revertSymbol(order.Contract),
			Side:     side,
			Type:     "LIMIT",
			Price:    price,
			Quantity: quantity,
			Status:   "NEW",
		})
	}

	// Also get trigger orders
	triggerOpts := &gateapi.ListPriceTriggeredOrdersOpts{
		Contract: optional.NewString(symbol),
	}

	triggerOrders, _, err := t.client.FuturesApi.ListPriceTriggeredOrders(t.ctx, "usdt", "open", triggerOpts)
	if err == nil {
		for _, order := range triggerOrders {
			triggerPrice, _ := strconv.ParseFloat(order.Trigger.Price, 64)

			side := "BUY"
			if order.Initial.Size < 0 {
				side = "SELL"
			}

			orderType := "STOP_MARKET"
			if order.Trigger.Rule == 2 {
				orderType = "TAKE_PROFIT_MARKET"
			}

			// Convert contract count to actual token quantity
			quantity := math.Abs(float64(order.Initial.Size)) * quantoMultiplier

			result = append(result, types.OpenOrder{
				OrderID:   fmt.Sprintf("%d", order.Id),
				Symbol:    t.revertSymbol(order.Initial.Contract),
				Side:      side,
				Type:      orderType,
				StopPrice: triggerPrice,
				Quantity:  quantity,
				Status:    "NEW",
			})
		}
	}

	return result, nil
}
