package gate

import (
	"fmt"
	"nofx/trader/types"
	"strconv"
	"time"

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

// GetBalance retrieves account balance
func (t *GateTrader) GetBalance() (map[string]interface{}, error) {
	// Check cache
	t.balanceCacheMutex.RLock()
	if t.cachedBalance != nil && time.Since(t.balanceCacheTime) < t.cacheDuration {
		cached := t.cachedBalance
		t.balanceCacheMutex.RUnlock()
		return cached, nil
	}
	t.balanceCacheMutex.RUnlock()

	// Fetch from API
	accounts, _, err := t.client.FuturesApi.ListFuturesAccounts(t.ctx, "usdt")
	if err != nil {
		return nil, fmt.Errorf("failed to get balance: %w", err)
	}

	total, err := types.ParseFloatField("total", accounts.Total)
	if err != nil {
		return nil, err
	}
	available, err := types.ParseFloatField("available", accounts.Available)
	if err != nil {
		return nil, err
	}
	unrealizedPnl, err := types.ParseFloatField("unrealisedPnl", accounts.UnrealisedPnl)
	if err != nil {
		return nil, err
	}

	result := map[string]interface{}{
		"totalWalletBalance":    total,
		"availableBalance":      available,
		"totalUnrealizedProfit": unrealizedPnl,
	}

	// Update cache
	t.balanceCacheMutex.Lock()
	t.cachedBalance = result
	t.balanceCacheTime = time.Now()
	t.balanceCacheMutex.Unlock()

	return result, nil
}

// GetPositions retrieves all open positions
func (t *GateTrader) GetPositions() ([]map[string]interface{}, error) {
	// Check cache
	t.positionsCacheMutex.RLock()
	if t.cachedPositions != nil && time.Since(t.positionsCacheTime) < t.cacheDuration {
		cached := t.cachedPositions
		t.positionsCacheMutex.RUnlock()
		return cached, nil
	}
	t.positionsCacheMutex.RUnlock()

	// Fetch from API
	positions, _, err := t.client.FuturesApi.ListPositions(t.ctx, "usdt", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get positions: %w", err)
	}

	var result []map[string]interface{}
	for _, pos := range positions {
		if pos.Size == 0 {
			continue // Skip empty positions
		}

		entryPrice, _ := strconv.ParseFloat(pos.EntryPrice, 64)
		markPrice, _ := strconv.ParseFloat(pos.MarkPrice, 64)
		liqPrice, _ := strconv.ParseFloat(pos.LiqPrice, 64)
		unrealizedPnl, _ := strconv.ParseFloat(pos.UnrealisedPnl, 64)
		leverage, _ := strconv.ParseFloat(pos.Leverage, 64)

		// Gate returns position size in contracts, need to convert to base currency
		// Each contract = quanto_multiplier base currency
		contractSize := float64(pos.Size)
		if pos.Size < 0 {
			contractSize = float64(-pos.Size)
		}

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

		// Convert contract count to actual token quantity
		positionAmt := contractSize * quantoMultiplier

		// Determine side based on position size
		side := "long"
		if pos.Size < 0 {
			side = "short"
		}

		result = append(result, map[string]interface{}{
			"symbol":           pos.Contract,
			"positionAmt":      positionAmt,
			"entryPrice":       entryPrice,
			"markPrice":        markPrice,
			"unRealizedProfit": unrealizedPnl,
			"leverage":         int(leverage),
			"liquidationPrice": liqPrice,
			"side":             side,
		})
	}

	// Update cache
	t.positionsCacheMutex.Lock()
	t.cachedPositions = result
	t.positionsCacheTime = time.Now()
	t.positionsCacheMutex.Unlock()

	return result, nil
}

// GetClosedPnL retrieves closed position PnL records
func (t *GateTrader) GetClosedPnL(startTime time.Time, limit int) ([]types.ClosedPnLRecord, error) {
	if limit <= 0 {
		limit = 100
	}
	if limit > 100 {
		limit = 100
	}

	opts := &gateapi.ListPositionCloseOpts{
		Limit: optional.NewInt32(int32(limit)),
		From:  optional.NewInt64(startTime.Unix()),
	}

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

	records := make([]types.ClosedPnLRecord, 0, len(closedPositions))
	for _, pos := range closedPositions {
		pnl, _ := strconv.ParseFloat(pos.Pnl, 64)

		record := types.ClosedPnLRecord{
			Symbol:      t.revertSymbol(pos.Contract),
			Side:        pos.Side,
			RealizedPnL: pnl,
			ExitTime:    time.Unix(int64(pos.Time), 0).UTC(),
			CloseType:   "unknown",
		}

		records = append(records, record)
	}

	return records, nil
}
