package hyperliquid

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

	hl "github.com/sonirico/go-hyperliquid"
)

// GetBalance gets account balance
func (t *HyperliquidTrader) GetBalance() (map[string]interface{}, error) {
	logger.Infof("🔄 Calling Hyperliquid API to get account balance...")

	// Step 1: Query Spot account balance
	spotState, err := t.exchange.Info().SpotUserState(t.ctx, t.walletAddr)
	var spotUSDCBalance float64 = 0.0
	if err != nil {
		logger.Infof("⚠️ Failed to query Spot balance (may have no spot assets): %v", err)
	} else if spotState != nil && len(spotState.Balances) > 0 {
		for _, balance := range spotState.Balances {
			if balance.Coin == "USDC" {
				spotUSDCBalance, _ = strconv.ParseFloat(balance.Total, 64)
				logger.Infof("✓ Found Spot balance: %.2f USDC", spotUSDCBalance)
				break
			}
		}
	}

	// Step 2: Query Perpetuals contract account status
	accountState, err := t.exchange.Info().UserState(t.ctx, t.walletAddr)
	if err != nil {
		logger.Infof("❌ Hyperliquid Perpetuals API call failed: %v", err)
		return nil, fmt.Errorf("failed to get account information: %w", err)
	}

	// Parse balance information (MarginSummary fields are all strings)
	result := make(map[string]interface{})

	// Step 3: Dynamically select correct summary based on margin mode (CrossMarginSummary or MarginSummary)
	var accountValue, totalMarginUsed float64
	var summaryType string
	var summary interface{}

	var parseErr error
	if t.isCrossMargin {
		// Cross margin mode: use CrossMarginSummary
		if accountValue, parseErr = types.ParseFloatField("accountValue", accountState.CrossMarginSummary.AccountValue); parseErr != nil {
			return nil, parseErr
		}
		if totalMarginUsed, parseErr = types.ParseFloatField("totalMarginUsed", accountState.CrossMarginSummary.TotalMarginUsed); parseErr != nil {
			return nil, parseErr
		}
		summaryType = "CrossMarginSummary (cross margin)"
		summary = accountState.CrossMarginSummary
	} else {
		// Isolated margin mode: use MarginSummary
		if accountValue, parseErr = types.ParseFloatField("accountValue", accountState.MarginSummary.AccountValue); parseErr != nil {
			return nil, parseErr
		}
		if totalMarginUsed, parseErr = types.ParseFloatField("totalMarginUsed", accountState.MarginSummary.TotalMarginUsed); parseErr != nil {
			return nil, parseErr
		}
		summaryType = "MarginSummary (isolated margin)"
		summary = accountState.MarginSummary
	}

	// Debug: Print complete summary structure returned by API
	summaryJSON, _ := json.MarshalIndent(summary, "  ", "  ")
	logger.Infof("🔍 [DEBUG] Hyperliquid API %s complete data:", summaryType)
	logger.Infof("%s", string(summaryJSON))

	// Critical fix: Accumulate actual unrealized PnL from all positions
	totalUnrealizedPnl := 0.0
	for _, assetPos := range accountState.AssetPositions {
		unrealizedPnl, _ := strconv.ParseFloat(assetPos.Position.UnrealizedPnl, 64)
		totalUnrealizedPnl += unrealizedPnl
	}

	// Correctly understand Hyperliquid fields:
	// AccountValue = Total account equity (includes idle funds + position value + unrealized PnL)
	// TotalMarginUsed = Margin used by positions (included in AccountValue, for display only)
	//
	// To be compatible with auto_types.go calculation logic (totalEquity = totalWalletBalance + totalUnrealizedProfit)
	// Need to return "wallet balance without unrealized PnL"
	walletBalanceWithoutUnrealized := accountValue - totalUnrealizedPnl

	// Step 4: Use Withdrawable field (PR #443)
	// Withdrawable is the official real withdrawable balance, more reliable than simple calculation
	availableBalance := 0.0
	if accountState.Withdrawable != "" {
		withdrawable, err := strconv.ParseFloat(accountState.Withdrawable, 64)
		if err == nil && withdrawable > 0 {
			availableBalance = withdrawable
			logger.Infof("✓ Using Withdrawable as available balance: %.2f", availableBalance)
		}
	}

	// Fallback: If no Withdrawable, use simple calculation
	if availableBalance == 0 && accountState.Withdrawable == "" {
		availableBalance = accountValue - totalMarginUsed
		if availableBalance < 0 {
			logger.Infof("⚠️ Calculated available balance is negative (%.2f), reset to 0", availableBalance)
			availableBalance = 0
		}
	}

	// Step 5: Query xyz dex balance (stock perps, forex, commodities)
	var xyzAccountValue, xyzUnrealizedPnl float64
	var xyzPositions []xyzAssetPosition
	xyzAccountValue, xyzUnrealizedPnl, xyzPositions, err = t.getXYZDexBalance()
	if err != nil {
		// xyz dex query failed - log warning but don't fail the entire balance query
		logger.Infof("⚠️ Failed to query xyz dex balance: %v", err)
	}
	// Always log xyz dex state for debugging
	logger.Infof("🔍 xyz dex state: accountValue=%.4f, unrealizedPnl=%.4f, positions=%d",
		xyzAccountValue, xyzUnrealizedPnl, len(xyzPositions))
	for _, pos := range xyzPositions {
		entryPx := "nil"
		if pos.Position.EntryPx != nil {
			entryPx = *pos.Position.EntryPx
		}
		logger.Infof("   └─ %s: size=%s, entryPx=%s, posValue=%s, pnl=%s",
			pos.Position.Coin, pos.Position.Szi, entryPx, pos.Position.PositionValue, pos.Position.UnrealizedPnl)
	}
	xyzMarginUsed := calculateXYZMarginUsed(xyzPositions)
	balanceBreakdown := calculateHyperliquidBalanceBreakdown(
		t.isUnifiedAccount,
		spotUSDCBalance,
		accountValue,
		totalUnrealizedPnl,
		totalMarginUsed,
		availableBalance,
		xyzAccountValue,
		xyzUnrealizedPnl,
		xyzMarginUsed,
	)
	totalWalletBalance := balanceBreakdown.TotalWalletBalance
	totalUnrealizedPnlAll := balanceBreakdown.TotalUnrealizedProfit
	totalEquityCalculated := balanceBreakdown.TotalEquity
	availableBalance = balanceBreakdown.AvailableBalance

	// Step 7: Unified Account mode - Spot USDC is used as collateral for Perps
	// In this mode, xyz/core account values are collateral views backed by the
	// same Spot USDC. They must not be added on top of Spot or the dashboard
	// will double count equity after a position opens.
	if t.isUnifiedAccount && spotUSDCBalance > 0 {
		logger.Infof("✓ Unified Account: Spot %.2f USDC used as shared collateral (available: %.2f)",
			spotUSDCBalance, availableBalance)
	}

	// Suppress unused variable warning
	_ = totalUnrealizedPnlAll

	result["totalWalletBalance"] = totalWalletBalance       // Total assets (Perp + Spot + xyz) - unrealized
	result["totalEquity"] = totalEquityCalculated           // Total equity = Perp AV + Spot + xyz AV
	result["availableBalance"] = availableBalance           // Available balance (Perp + Spot if unified)
	result["totalUnrealizedProfit"] = totalUnrealizedPnlAll // Unrealized PnL (Perpetuals + xyz)
	result["spotBalance"] = spotUSDCBalance                 // Spot balance
	result["xyzDexBalance"] = xyzAccountValue               // xyz dex equity (stock perps, forex, commodities)
	result["xyzDexUnrealizedPnl"] = xyzUnrealizedPnl        // xyz dex unrealized PnL
	result["perpAccountValue"] = accountValue               // Perp account value for debugging
	result["totalMarginUsed"] = balanceBreakdown.TotalMarginUsed

	logger.Infof("✓ Hyperliquid complete account:")
	logger.Infof("  • Spot balance: %.2f USDC", spotUSDCBalance)
	logger.Infof("  • Perpetuals equity: %.2f USDC (wallet %.2f + unrealized %.2f)",
		accountValue,
		walletBalanceWithoutUnrealized,
		totalUnrealizedPnl)
	logger.Infof("  • Perpetuals available balance: %.2f USDC", availableBalance)
	logger.Infof("  • Margin used: %.2f USDC", totalMarginUsed)
	logger.Infof("  • xyz dex equity: %.2f USDC (wallet %.2f + unrealized %.2f)",
		xyzAccountValue,
		balanceBreakdown.XYZWalletBalance,
		xyzUnrealizedPnl)
	logger.Infof("  • Total wallet balance: %.2f USDC", totalWalletBalance)
	logger.Infof("  ⭐ Total equity: %.2f USDC | Available: %.2f | Spot: %.2f | xyz view: %.2f",
		totalEquityCalculated, availableBalance, spotUSDCBalance, xyzAccountValue)

	return result, nil
}

type hyperliquidBalanceBreakdown struct {
	TotalWalletBalance    float64
	TotalEquity           float64
	AvailableBalance      float64
	TotalUnrealizedProfit float64
	TotalMarginUsed       float64
	PerpWalletBalance     float64
	XYZWalletBalance      float64
}

func calculateHyperliquidBalanceBreakdown(
	isUnifiedAccount bool,
	spotUSDCBalance float64,
	perpAccountValue float64,
	perpUnrealizedPnl float64,
	perpMarginUsed float64,
	perpWithdrawable float64,
	xyzAccountValue float64,
	xyzUnrealizedPnl float64,
	xyzMarginUsed float64,
) hyperliquidBalanceBreakdown {
	perpWalletBalance := perpAccountValue - perpUnrealizedPnl
	xyzWalletBalance := xyzAccountValue - xyzUnrealizedPnl
	totalUnrealizedPnl := perpUnrealizedPnl + xyzUnrealizedPnl
	totalMarginUsed := perpMarginUsed + xyzMarginUsed

	if isUnifiedAccount && spotUSDCBalance > 0 {
		totalEquity := spotUSDCBalance + totalUnrealizedPnl
		availableBalance := totalEquity - totalMarginUsed
		if availableBalance < 0 {
			availableBalance = 0
		}
		return hyperliquidBalanceBreakdown{
			TotalWalletBalance:    spotUSDCBalance,
			TotalEquity:           totalEquity,
			AvailableBalance:      availableBalance,
			TotalUnrealizedProfit: totalUnrealizedPnl,
			TotalMarginUsed:       totalMarginUsed,
			PerpWalletBalance:     perpWalletBalance,
			XYZWalletBalance:      xyzWalletBalance,
		}
	}

	availableBalance := perpWithdrawable
	if availableBalance == 0 {
		availableBalance = perpAccountValue - perpMarginUsed
	}
	if availableBalance < 0 {
		availableBalance = 0
	}

	return hyperliquidBalanceBreakdown{
		TotalWalletBalance:    perpWalletBalance + spotUSDCBalance + xyzWalletBalance,
		TotalEquity:           perpAccountValue + spotUSDCBalance + xyzAccountValue,
		AvailableBalance:      availableBalance,
		TotalUnrealizedProfit: totalUnrealizedPnl,
		TotalMarginUsed:       totalMarginUsed,
		PerpWalletBalance:     perpWalletBalance,
		XYZWalletBalance:      xyzWalletBalance,
	}
}

func calculateXYZMarginUsed(positions []xyzAssetPosition) float64 {
	total := 0.0
	for _, pos := range positions {
		positionValue, _ := strconv.ParseFloat(pos.Position.PositionValue, 64)
		if positionValue < 0 {
			positionValue = -positionValue
		}
		leverage := float64(pos.Position.Leverage.Value)
		if leverage <= 0 {
			leverage = 1
		}
		total += positionValue / leverage
	}
	return total
}

// xyzDexState represents the clearinghouse state for xyz dex
type xyzDexState struct {
	MarginSummary      *xyzMarginSummary  `json:"marginSummary,omitempty"`
	CrossMarginSummary *xyzMarginSummary  `json:"crossMarginSummary,omitempty"`
	Withdrawable       string             `json:"withdrawable,omitempty"`
	AssetPositions     []xyzAssetPosition `json:"assetPositions,omitempty"`
}

type xyzMarginSummary struct {
	AccountValue    string `json:"accountValue"`
	TotalMarginUsed string `json:"totalMarginUsed"`
}

type xyzAssetPosition struct {
	Position struct {
		Coin          string  `json:"coin"`
		Szi           string  `json:"szi"`
		EntryPx       *string `json:"entryPx"`
		PositionValue string  `json:"positionValue"`
		UnrealizedPnl string  `json:"unrealizedPnl"`
		LiquidationPx *string `json:"liquidationPx"`
		Leverage      struct {
			Type  string `json:"type"`
			Value int    `json:"value"`
		} `json:"leverage"`
	} `json:"position"`
}

// getXYZDexBalance queries the xyz dex balance (stock perps, forex, commodities)
func (t *HyperliquidTrader) getXYZDexBalance() (accountValue float64, unrealizedPnl float64, positions []xyzAssetPosition, err error) {
	// Build request for xyz dex clearinghouse state
	reqBody := map[string]interface{}{
		"type": "clearinghouseState",
		"user": t.walletAddr,
		"dex":  "xyz",
	}

	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return 0, 0, nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	// Determine API URL
	apiURL := "https://api.hyperliquid.xyz/info"
	// Note: xyz dex may not be available on testnet

	req, err := http.NewRequestWithContext(t.ctx, "POST", apiURL, bytes.NewBuffer(jsonBody))
	if err != nil {
		return 0, 0, nil, fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return 0, 0, nil, fmt.Errorf("failed to execute request: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0, 0, nil, fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return 0, 0, nil, fmt.Errorf("xyz dex API error (status %d): %s", resp.StatusCode, string(body))
	}

	var state xyzDexState
	if err := json.Unmarshal(body, &state); err != nil {
		return 0, 0, nil, fmt.Errorf("failed to parse response: %w", err)
	}

	// Parse account value - xyz dex uses MarginSummary for isolated margin mode
	// CrossMarginSummary may exist but with 0 values, so check MarginSummary first
	if state.MarginSummary != nil && state.MarginSummary.AccountValue != "" {
		av, _ := strconv.ParseFloat(state.MarginSummary.AccountValue, 64)
		if av > 0 {
			accountValue = av
		}
	}
	// Fallback to CrossMarginSummary if MarginSummary is 0
	if accountValue == 0 && state.CrossMarginSummary != nil && state.CrossMarginSummary.AccountValue != "" {
		accountValue, _ = strconv.ParseFloat(state.CrossMarginSummary.AccountValue, 64)
	}

	// Calculate total unrealized PnL from positions
	for _, pos := range state.AssetPositions {
		pnl, _ := strconv.ParseFloat(pos.Position.UnrealizedPnl, 64)
		unrealizedPnl += pnl
	}

	return accountValue, unrealizedPnl, state.AssetPositions, nil
}

// GetMarketPrice gets market price (supports both crypto and xyz dex assets)
func (t *HyperliquidTrader) GetMarketPrice(symbol string) (float64, error) {
	coin := convertSymbolToHyperliquid(symbol)

	// Check if this is an xyz dex asset
	if strings.HasPrefix(coin, "xyz:") {
		return t.getXyzMarketPrice(coin)
	}

	// Get all market prices for crypto
	allMids, err := t.exchange.Info().AllMids(t.ctx)
	if err != nil {
		return 0, fmt.Errorf("failed to get price: %w", err)
	}

	// Find price for corresponding coin (allMids is map[string]string)
	if priceStr, ok := allMids[coin]; ok {
		priceFloat, err := strconv.ParseFloat(priceStr, 64)
		if err == nil {
			return priceFloat, nil
		}
		return 0, fmt.Errorf("price format error: %v", err)
	}

	return 0, fmt.Errorf("price not found for %s", symbol)
}

// getXyzMarketPrice gets market price for xyz dex assets
func (t *HyperliquidTrader) getXyzMarketPrice(coin string) (float64, error) {
	// Build request for xyz dex allMids
	reqBody := map[string]string{
		"type": "allMids",
		"dex":  "xyz",
	}

	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return 0, fmt.Errorf("failed to marshal request: %w", err)
	}

	apiURL := "https://api.hyperliquid.xyz/info"

	req, err := http.NewRequestWithContext(t.ctx, "POST", apiURL, bytes.NewBuffer(jsonBody))
	if err != nil {
		return 0, fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return 0, fmt.Errorf("failed to execute request: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0, fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return 0, fmt.Errorf("xyz dex allMids API error (status %d): %s", resp.StatusCode, string(body))
	}

	var mids map[string]string
	if err := json.Unmarshal(body, &mids); err != nil {
		return 0, fmt.Errorf("failed to parse response: %w", err)
	}

	// The API returns keys with xyz: prefix, so ensure the coin has it
	lookupKey := coin
	if !strings.HasPrefix(lookupKey, "xyz:") {
		lookupKey = "xyz:" + lookupKey
	}

	if priceStr, ok := mids[lookupKey]; ok {
		priceFloat, err := strconv.ParseFloat(priceStr, 64)
		if err == nil {
			return priceFloat, nil
		}
		return 0, fmt.Errorf("price format error: %v", err)
	}

	return 0, fmt.Errorf("xyz dex price not found for %s (lookup key: %s)", coin, lookupKey)
}

// GetOrderStatus gets order status
// Hyperliquid uses IOC orders, usually filled or cancelled immediately
// For completed orders, need to query historical records
func (t *HyperliquidTrader) GetOrderStatus(symbol string, orderID string) (map[string]interface{}, error) {
	// Hyperliquid's IOC orders are completed almost immediately
	// If order was placed through this system, returned status will be FILLED
	// Try to query open orders to determine if still pending
	coin := convertSymbolToHyperliquid(symbol)

	// First check if in open orders
	openOrders, err := t.exchange.Info().OpenOrders(t.ctx, t.walletAddr)
	if err != nil {
		// If query fails, assume order is completed
		return map[string]interface{}{
			"orderId":     orderID,
			"status":      "FILLED",
			"avgPrice":    0.0,
			"executedQty": 0.0,
			"commission":  0.0,
		}, nil
	}

	// Check if order is in open orders list
	for _, order := range openOrders {
		if order.Coin == coin && fmt.Sprintf("%d", order.Oid) == orderID {
			// Order is still pending
			return map[string]interface{}{
				"orderId":     orderID,
				"status":      "NEW",
				"avgPrice":    0.0,
				"executedQty": 0.0,
				"commission":  0.0,
			}, nil
		}
	}

	// Order not in open list, meaning completed or cancelled
	// Hyperliquid IOC orders not in open list are usually filled
	return map[string]interface{}{
		"orderId":     orderID,
		"status":      "FILLED",
		"avgPrice":    0.0, // Hyperliquid does not directly return execution price, need to get from position info
		"executedQty": 0.0,
		"commission":  0.0,
	}, nil
}

// GetClosedPnL gets recent closing trades from Hyperliquid
// Note: Hyperliquid does NOT have a position history API, only fill history.
// This returns individual closing trades for real-time position closure detection.
func (t *HyperliquidTrader) 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 (Hyperliquid uses one-way mode)
		side := "long"
		if trade.Side == "SELL" || trade.Side == "Sell" {
			side = "long" // Selling closes long
		} else {
			side = "short" // Buying closes 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
}

// GetTrades retrieves trade history from Hyperliquid
func (t *HyperliquidTrader) GetTrades(startTime time.Time, limit int) ([]types.TradeRecord, error) {
	// Use UserFills (returns up to 2000 recent fills) rather than
	// UserFillsByTime, which is hard-capped at 100 fills per response. At
	// this trading frequency the account exceeds 100 fills/24h, so
	// UserFillsByTime silently dropped ~20% of fills — skewing recorded PnL
	// and fees away from the exchange truth. 2000 recent fills covers many
	// days of history; we filter to startTime client-side.
	startTimeMs := startTime.UnixMilli()
	fills, err := t.exchange.Info().UserFills(t.ctx, hl.UserFillsParams{Address: t.walletAddr})
	if err != nil {
		return nil, fmt.Errorf("failed to get user fills: %w", err)
	}

	var trades []types.TradeRecord
	for _, fill := range fills {
		if fill.Time < startTimeMs {
			continue
		}
		price, _ := strconv.ParseFloat(fill.Price, 64)
		qty, _ := strconv.ParseFloat(fill.Size, 64)
		fee, _ := strconv.ParseFloat(fill.Fee, 64)
		pnl, _ := strconv.ParseFloat(fill.ClosedPnl, 64)

		// Determine side: "B" = Buy, "S" = Sell (or "A" = Ask, "B" = Bid)
		var side string
		if fill.Side == "B" || fill.Side == "Buy" || fill.Side == "bid" {
			side = "BUY"
		} else {
			side = "SELL"
		}

		// Parse Dir field to get order action
		// Hyperliquid Dir values: "Open Long", "Open Short", "Close Long", "Close Short"
		var orderAction string
		switch strings.ToLower(fill.Dir) {
		case "open long":
			orderAction = "open_long"
		case "open short":
			orderAction = "open_short"
		case "close long":
			orderAction = "close_long"
		case "close short":
			orderAction = "close_short"
		default:
			// Fallback: use RealizedPnL if Dir is missing/unknown
			if pnl != 0 {
				if side == "BUY" {
					orderAction = "close_short"
				} else {
					orderAction = "close_long"
				}
			} else {
				if side == "BUY" {
					orderAction = "open_long"
				} else {
					orderAction = "open_short"
				}
			}
		}

		// Hyperliquid uses one-way mode, so PositionSide is "BOTH"
		trade := types.TradeRecord{
			TradeID:      strconv.FormatInt(fill.Tid, 10),
			Symbol:       fill.Coin,
			Side:         side,
			PositionSide: "BOTH", // Hyperliquid doesn't have hedge mode
			OrderAction:  orderAction,
			Price:        price,
			Quantity:     qty,
			RealizedPnL:  pnl,
			Fee:          fee,
			Time:         time.UnixMilli(fill.Time).UTC(),
		}
		trades = append(trades, trade)
	}

	return trades, nil
}

// GetOpenOrders gets all open/pending orders for a symbol
func (t *HyperliquidTrader) GetOpenOrders(symbol string) ([]types.OpenOrder, error) {
	openOrders, err := t.exchange.Info().OpenOrders(t.ctx, t.walletAddr)
	if err != nil {
		return nil, fmt.Errorf("failed to get open orders: %w", err)
	}

	var result []types.OpenOrder
	for _, order := range openOrders {
		if order.Coin != symbol {
			continue
		}

		side := "BUY"
		if order.Side == "A" {
			side = "SELL"
		}

		result = append(result, types.OpenOrder{
			OrderID:      fmt.Sprintf("%d", order.Oid),
			Symbol:       order.Coin,
			Side:         side,
			PositionSide: "",
			Type:         "LIMIT",
			Price:        order.LimitPx,
			StopPrice:    0,
			Quantity:     order.Size,
			Status:       "NEW",
		})
	}

	return result, nil
}

// GetOrderBook gets the order book for a symbol
// Implements GridTrader interface
func (t *HyperliquidTrader) GetOrderBook(symbol string, depth int) (bids, asks [][]float64, err error) {
	coin := convertSymbolToHyperliquid(symbol)

	l2Book, err := t.exchange.Info().L2Snapshot(t.ctx, coin)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to get order book: %w", err)
	}

	if l2Book == nil || len(l2Book.Levels) < 2 {
		return nil, nil, fmt.Errorf("invalid order book data")
	}

	// Parse bids (first level array)
	for i, level := range l2Book.Levels[0] {
		if i >= depth {
			break
		}
		bids = append(bids, []float64{level.Px, level.Sz})
	}

	// Parse asks (second level array)
	for i, level := range l2Book.Levels[1] {
		if i >= depth {
			break
		}
		asks = append(asks, []float64{level.Px, level.Sz})
	}

	return bids, asks, nil
}
