package aster

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"nofx/logger"
	"nofx/trader/types"
	"strconv"
	"time"
)

// GetBalance Get account balance
func (t *AsterTrader) GetBalance() (map[string]interface{}, error) {
	params := make(map[string]interface{})
	body, err := t.request("GET", "/fapi/v3/balance", params)
	if err != nil {
		return nil, err
	}

	var balances []map[string]interface{}
	if err := json.Unmarshal(body, &balances); err != nil {
		return nil, err
	}

	// Find USDT balance
	availableBalance := 0.0
	crossUnPnl := 0.0
	crossWalletBalance := 0.0
	foundUSDT := false

	for _, bal := range balances {
		if asset, ok := bal["asset"].(string); ok && asset == "USDT" {
			foundUSDT = true

			// Parse Aster fields (reference: https://github.com/asterdex/api-docs)
			var parseErr error
			if avail, ok := bal["availableBalance"].(string); ok {
				if availableBalance, parseErr = types.ParseFloatField("availableBalance", avail); parseErr != nil {
					return nil, parseErr
				}
			}
			if unpnl, ok := bal["crossUnPnl"].(string); ok {
				if crossUnPnl, parseErr = types.ParseFloatField("crossUnPnl", unpnl); parseErr != nil {
					return nil, parseErr
				}
			}
			if cwb, ok := bal["crossWalletBalance"].(string); ok {
				if crossWalletBalance, parseErr = types.ParseFloatField("crossWalletBalance", cwb); parseErr != nil {
					return nil, parseErr
				}
			}
			break
		}
	}

	if !foundUSDT {
		logger.Infof("⚠️  USDT asset record not found!")
	}

	// Get positions to calculate margin used and real unrealized PnL
	positions, err := t.GetPositions()
	if err != nil {
		logger.Infof("⚠️  Failed to get position information: %v", err)
		// fallback: use simple calculation when unable to get positions
		return map[string]interface{}{
			"totalWalletBalance":    crossWalletBalance,
			"availableBalance":      availableBalance,
			"totalUnrealizedProfit": crossUnPnl,
		}, nil
	}

	// Critical fix: accumulate real unrealized PnL from positions
	// Aster's crossUnPnl field is inaccurate, need to recalculate from position data
	totalMarginUsed := 0.0
	realUnrealizedPnl := 0.0
	for _, pos := range positions {
		markPrice := pos["markPrice"].(float64)
		quantity := pos["positionAmt"].(float64)
		if quantity < 0 {
			quantity = -quantity
		}
		unrealizedPnl := pos["unRealizedProfit"].(float64)
		realUnrealizedPnl += unrealizedPnl

		leverage := 10
		if lev, ok := pos["leverage"].(float64); ok {
			leverage = int(lev)
		}
		marginUsed := (quantity * markPrice) / float64(leverage)
		totalMarginUsed += marginUsed
	}

	// Aster correct calculation method:
	// Total equity = available balance + margin used
	// Wallet balance = total equity - unrealized PnL
	// Unrealized PnL = calculated from accumulated positions (don't use API's crossUnPnl)
	totalEquity := availableBalance + totalMarginUsed
	totalWalletBalance := totalEquity - realUnrealizedPnl

	return map[string]interface{}{
		"totalWalletBalance":    totalWalletBalance, // Wallet balance (excluding unrealized PnL)
		"availableBalance":      availableBalance,   // Available balance
		"totalUnrealizedProfit": realUnrealizedPnl,  // Unrealized PnL (accumulated from positions)
	}, nil
}

// GetMarketPrice Get market price
func (t *AsterTrader) GetMarketPrice(symbol string) (float64, error) {
	// Use ticker interface to get current price
	resp, err := t.client.Get(fmt.Sprintf("%s/fapi/v3/ticker/price?symbol=%s", t.baseURL, symbol))
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return 0, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
	}

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return 0, err
	}

	priceStr, ok := result["price"].(string)
	if !ok {
		return 0, errors.New("unable to get price")
	}

	return strconv.ParseFloat(priceStr, 64)
}

// GetClosedPnL gets recent closing trades from Aster
// Note: Aster does NOT have a position history API, only trade history.
// This returns individual closing trades for real-time position closure detection.
func (t *AsterTrader) GetClosedPnL(startTime time.Time, limit int) ([]types.ClosedPnLRecord, error) {
	trades, err := t.GetTrades(startTime, limit)
	if err != nil {
		return nil, err
	}

	// Filter only closing trades (realizedPnl != 0)
	var records []types.ClosedPnLRecord
	for _, trade := range trades {
		if trade.RealizedPnL == 0 {
			continue
		}

		// Determine side from PositionSide or trade direction
		side := "long"
		if trade.PositionSide == "SHORT" || trade.PositionSide == "short" {
			side = "short"
		} else if trade.PositionSide == "BOTH" || trade.PositionSide == "" {
			if trade.Side == "SELL" || trade.Side == "Sell" {
				side = "long"
			} else {
				side = "short"
			}
		}

		// Calculate entry price from PnL
		var entryPrice float64
		if trade.Quantity > 0 {
			if side == "long" {
				entryPrice = trade.Price - trade.RealizedPnL/trade.Quantity
			} else {
				entryPrice = trade.Price + trade.RealizedPnL/trade.Quantity
			}
		}

		records = append(records, types.ClosedPnLRecord{
			Symbol:      trade.Symbol,
			Side:        side,
			EntryPrice:  entryPrice,
			ExitPrice:   trade.Price,
			Quantity:    trade.Quantity,
			RealizedPnL: trade.RealizedPnL,
			Fee:         trade.Fee,
			ExitTime:    trade.Time,
			EntryTime:   trade.Time,
			OrderID:     trade.TradeID,
			ExchangeID:  trade.TradeID,
			CloseType:   "unknown",
		})
	}

	return records, nil
}

// AsterTradeRecord represents a trade from Aster API
type AsterTradeRecord struct {
	ID           int64  `json:"id"`
	Symbol       string `json:"symbol"`
	OrderID      int64  `json:"orderId"`
	Side         string `json:"side"`         // BUY or SELL
	PositionSide string `json:"positionSide"` // LONG or SHORT
	Price        string `json:"price"`
	Qty          string `json:"qty"`
	RealizedPnl  string `json:"realizedPnl"`
	Commission   string `json:"commission"`
	Time         int64  `json:"time"`
	Buyer        bool   `json:"buyer"`
	Maker        bool   `json:"maker"`
}

// GetTrades retrieves trade history from Aster
func (t *AsterTrader) GetTrades(startTime time.Time, limit int) ([]types.TradeRecord, error) {
	if limit <= 0 {
		limit = 500
	}

	// Build request params
	params := map[string]interface{}{
		"startTime": startTime.UnixMilli(),
		"limit":     limit,
	}

	// Use existing request method with signing
	body, err := t.request("GET", "/fapi/v3/userTrades", params)
	if err != nil {
		logger.Infof("⚠️  Aster userTrades API error: %v", err)
		return []types.TradeRecord{}, nil
	}

	var asterTrades []AsterTradeRecord
	if err := json.Unmarshal(body, &asterTrades); err != nil {
		logger.Infof("⚠️  Failed to parse Aster trades response: %v", err)
		return []types.TradeRecord{}, nil
	}

	// Convert to unified TradeRecord format
	var result []types.TradeRecord
	for _, at := range asterTrades {
		price, _ := strconv.ParseFloat(at.Price, 64)
		qty, _ := strconv.ParseFloat(at.Qty, 64)
		fee, _ := strconv.ParseFloat(at.Commission, 64)
		pnl, _ := strconv.ParseFloat(at.RealizedPnl, 64)

		trade := types.TradeRecord{
			TradeID:      strconv.FormatInt(at.ID, 10),
			Symbol:       at.Symbol,
			Side:         at.Side,
			PositionSide: at.PositionSide,
			Price:        price,
			Quantity:     qty,
			RealizedPnL:  pnl,
			Fee:          fee,
			Time:         time.UnixMilli(at.Time).UTC(),
		}
		result = append(result, trade)
	}

	return result, nil
}

// GetOrderBook gets the order book for a symbol
func (t *AsterTrader) GetOrderBook(symbol string, depth int) (bids, asks [][]float64, err error) {
	if depth <= 0 {
		depth = 20
	}

	// Aster uses public endpoint (no signature required)
	resp, err := t.client.Get(fmt.Sprintf("%s/fapi/v3/depth?symbol=%s&limit=%d", t.baseURL, symbol, depth))
	if err != nil {
		return nil, nil, fmt.Errorf("failed to fetch order book: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return nil, nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
	}

	var result struct {
		Bids [][]string `json:"bids"` // [[price, qty], ...]
		Asks [][]string `json:"asks"` // [[price, qty], ...]
	}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, nil, fmt.Errorf("failed to parse order book: %w", err)
	}

	// Convert string arrays to float64 arrays
	bids = make([][]float64, len(result.Bids))
	for i, bid := range result.Bids {
		if len(bid) >= 2 {
			price, _ := strconv.ParseFloat(bid[0], 64)
			qty, _ := strconv.ParseFloat(bid[1], 64)
			bids[i] = []float64{price, qty}
		}
	}

	asks = make([][]float64, len(result.Asks))
	for i, ask := range result.Asks {
		if len(ask) >= 2 {
			price, _ := strconv.ParseFloat(ask[0], 64)
			qty, _ := strconv.ParseFloat(ask[1], 64)
			asks[i] = []float64{price, qty}
		}
	}

	return bids, asks, nil
}
