package bybit

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"nofx/trader/types"
	"strconv"
	"time"
)

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

	// Call API
	params := map[string]interface{}{
		"accountType": "UNIFIED",
	}

	result, err := t.client.NewUtaBybitServiceWithParams(params).GetAccountWallet(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get Bybit balance: %w", err)
	}

	if result.RetCode != 0 {
		return nil, fmt.Errorf("Bybit API error: %s", result.RetMsg)
	}

	// Extract balance information
	resultData, ok := result.Result.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("Bybit balance return format error")
	}

	list, _ := resultData["list"].([]interface{})

	var totalEquity, availableBalance, totalWalletBalance, totalPerpUPL float64 = 0, 0, 0, 0

	if len(list) > 0 {
		account, _ := list[0].(map[string]interface{})
		var parseErr error
		if equityStr, ok := account["totalEquity"].(string); ok {
			if totalEquity, parseErr = types.ParseFloatField("totalEquity", equityStr); parseErr != nil {
				return nil, parseErr
			}
		}
		if availStr, ok := account["totalAvailableBalance"].(string); ok {
			if availableBalance, parseErr = types.ParseFloatField("totalAvailableBalance", availStr); parseErr != nil {
				return nil, parseErr
			}
		}
		// Bybit UNIFIED account wallet balance field
		if walletStr, ok := account["totalWalletBalance"].(string); ok {
			if totalWalletBalance, parseErr = types.ParseFloatField("totalWalletBalance", walletStr); parseErr != nil {
				return nil, parseErr
			}
		}
		// Bybit perpetual contract unrealized PnL
		if uplStr, ok := account["totalPerpUPL"].(string); ok {
			if totalPerpUPL, parseErr = types.ParseFloatField("totalPerpUPL", uplStr); parseErr != nil {
				return nil, parseErr
			}
		}
	}

	// If no totalWalletBalance, use totalEquity
	if totalWalletBalance == 0 {
		totalWalletBalance = totalEquity
	}

	balance := map[string]interface{}{
		"totalEquity":           totalEquity,
		"totalWalletBalance":    totalWalletBalance,
		"availableBalance":      availableBalance,
		"totalUnrealizedProfit": totalPerpUPL,
		"balance":               totalEquity, // Compatible with other exchange formats
	}

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

	return balance, nil
}

// GetClosedPnL retrieves closed position PnL records from Bybit via direct HTTP API
func (t *BybitTrader) GetClosedPnL(startTime time.Time, limit int) ([]types.ClosedPnLRecord, error) {
	// The Bybit SDK doesn't expose the closed-pnl endpoint, use direct HTTP call
	return t.getClosedPnLViaHTTP(startTime, limit)
}

// getClosedPnLViaHTTP makes direct HTTP call to Bybit API for closed PnL with proper signing
func (t *BybitTrader) getClosedPnLViaHTTP(startTime time.Time, limit int) ([]types.ClosedPnLRecord, error) {
	// Build query string
	queryParams := fmt.Sprintf("category=linear&startTime=%d&limit=%d", startTime.UnixMilli(), limit)
	url := "https://api.bybit.com/v5/position/closed-pnl?" + queryParams

	// Generate timestamp
	timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
	recvWindow := "5000"

	// Build signature payload: timestamp + api_key + recv_window + queryString
	signPayload := timestamp + t.apiKey + recvWindow + queryParams

	// Generate HMAC-SHA256 signature
	h := hmac.New(sha256.New, []byte(t.secretKey))
	h.Write([]byte(signPayload))
	signature := hex.EncodeToString(h.Sum(nil))

	// Create request
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	// Add Bybit V5 API headers
	req.Header.Set("X-BAPI-API-KEY", t.apiKey)
	req.Header.Set("X-BAPI-SIGN", signature)
	req.Header.Set("X-BAPI-SIGN-TYPE", "2")
	req.Header.Set("X-BAPI-TIMESTAMP", timestamp)
	req.Header.Set("X-BAPI-RECV-WINDOW", recvWindow)
	req.Header.Set("Content-Type", "application/json")

	// Use http.DefaultClient for the request
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to call Bybit API: %w", err)
	}
	defer resp.Body.Close()

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

	var result struct {
		RetCode int                    `json:"retCode"`
		RetMsg  string                 `json:"retMsg"`
		Result  map[string]interface{} `json:"result"`
	}

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

	if result.RetCode != 0 {
		return nil, fmt.Errorf("Bybit API error: %s", result.RetMsg)
	}

	return t.parseClosedPnLResult(result.Result)
}

// parseClosedPnLResult parses the closed PnL result from Bybit API
func (t *BybitTrader) parseClosedPnLResult(resultData interface{}) ([]types.ClosedPnLRecord, error) {
	data, ok := resultData.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("invalid result format")
	}

	list, _ := data["list"].([]interface{})
	var records []types.ClosedPnLRecord

	for _, item := range list {
		pnl, ok := item.(map[string]interface{})
		if !ok {
			continue
		}

		// Parse fields
		symbol, _ := pnl["symbol"].(string)
		side, _ := pnl["side"].(string)
		orderId, _ := pnl["orderId"].(string)

		avgEntryPriceStr, _ := pnl["avgEntryPrice"].(string)
		avgExitPriceStr, _ := pnl["avgExitPrice"].(string)
		qtyStr, _ := pnl["qty"].(string)
		closedPnLStr, _ := pnl["closedPnl"].(string)
		cumEntryValueStr, _ := pnl["cumEntryValue"].(string)
		cumExitValueStr, _ := pnl["cumExitValue"].(string)
		leverageStr, _ := pnl["leverage"].(string)
		createdTimeStr, _ := pnl["createdTime"].(string)
		updatedTimeStr, _ := pnl["updatedTime"].(string)

		avgEntryPrice, _ := strconv.ParseFloat(avgEntryPriceStr, 64)
		avgExitPrice, _ := strconv.ParseFloat(avgExitPriceStr, 64)
		qty, _ := strconv.ParseFloat(qtyStr, 64)
		closedPnL, _ := strconv.ParseFloat(closedPnLStr, 64)
		leverage, _ := strconv.ParseInt(leverageStr, 10, 64)
		createdTime, _ := strconv.ParseInt(createdTimeStr, 10, 64)
		updatedTime, _ := strconv.ParseInt(updatedTimeStr, 10, 64)

		// Calculate approximate fee from value difference
		cumEntryValue, _ := strconv.ParseFloat(cumEntryValueStr, 64)
		cumExitValue, _ := strconv.ParseFloat(cumExitValueStr, 64)
		expectedPnL := cumExitValue - cumEntryValue
		if side == "Sell" {
			expectedPnL = cumEntryValue - cumExitValue
		}
		fee := expectedPnL - closedPnL
		if fee < 0 {
			fee = 0
		}

		// Normalize side
		normalizedSide := "long"
		if side == "Sell" {
			normalizedSide = "short"
		}

		record := types.ClosedPnLRecord{
			Symbol:      symbol,
			Side:        normalizedSide,
			EntryPrice:  avgEntryPrice,
			ExitPrice:   avgExitPrice,
			Quantity:    qty,
			RealizedPnL: closedPnL,
			Fee:         fee,
			Leverage:    int(leverage),
			EntryTime:   time.UnixMilli(createdTime).UTC(),
			ExitTime:    time.UnixMilli(updatedTime).UTC(),
			OrderID:     orderId,
			CloseType:   "unknown", // Bybit doesn't provide close type directly
			ExchangeID:  orderId,   // Use orderId as exchange ID
		}

		records = append(records, record)
	}

	return records, nil
}
