#!/usr/bin/env python3
"""
Import positions depuis The Graph API (Arbitrum)
SOLUTION AUTOMATIQUE FINALE
"""

import requests
import json
from datetime import datetime
from typing import List, Dict
import os
from dotenv import load_dotenv

load_dotenv()

WALLET_ADDRESS = os.getenv('WALLET_ADDRESS', '0x7476737C49d55bea691e54c2c96fD39a6bAf3049').lower()

# The Graph Gateway sur Arbitrum (trouvé via DevTools!)
THEGRAPH_URL = "https://gateway-arbitrum.network.thegraph.com/api/6edc89d15e0bffdb56057765735dfdc8/subgraphs/id/HxvztN7GmfYYDPb89K4FFYKFkRvBtscxYQqvX79D4XSQ"

# GraphQL query
GRAPHQL_QUERY = """
query OrderAndTradeHistorys($skip: Int, $first: Int, $user: String) {
    orderAndTradeHistories(
        skip: $skip
        first: $first
        orderBy: timestamp
        orderDirection: desc
        where: {
            user: $user
            actionType_not: ADD_MARGIN
        }
    ) {
        id
        orderOrTradeHash
        actionType
        timestamp
        txHash
        limitOrder {
            pair {
                name
            }
            isLong
            token {
                symbol
            }
            amountIn
            limitPrice
            qty
            takeProfit
            stopLoss
        }
        trade {
            pair {
                name
            }
            isLong
            token {
                symbol
            }
            margin
            entryPrice
            qty
            openFee
            executionFee
            closeType
            closePrice
            closeFee
            fundingFee
            holdingFee
            pnl
            actualPnl
        }
    }
}
"""


def fetch_all_history() -> List[Dict]:
    """Récupère TOUT l'historique en paginant"""

    print("=" * 80)
    print("📥 IMPORT DEPUIS THE GRAPH (ARBITRUM)")
    print("=" * 80)
    print()
    print(f"Wallet: {WALLET_ADDRESS}")
    print(f"Endpoint: {THEGRAPH_URL[:60]}...\n")

    all_histories = []
    skip = 0
    batch_size = 1000  # Max par requête

    while True:
        print(f"📡 Récupération batch {skip // batch_size + 1} (skip: {skip})...")

        variables = {
            "user": WALLET_ADDRESS,
            "skip": skip,
            "first": batch_size
        }

        try:
            response = requests.post(
                THEGRAPH_URL,
                json={
                    'query': GRAPHQL_QUERY,
                    'variables': variables
                },
                headers={
                    'Content-Type': 'application/json',
                    'Accept': '*/*',
                    'Origin': 'https://www.apollox.finance',
                    'Referer': 'https://www.apollox.finance/',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36'
                },
                timeout=30
            )

            response.raise_for_status()
            data = response.json()

            if 'errors' in data:
                print(f"❌ Erreur GraphQL: {data['errors']}")
                break

            histories = data.get('data', {}).get('orderAndTradeHistories', [])

            if not histories:
                print(f"   ✅ Fin (aucune donnée)")
                break

            print(f"   ✅ {len(histories)} entrées")
            all_histories.extend(histories)

            # Si moins que batch_size, on a tout récupéré
            if len(histories) < batch_size:
                break

            skip += batch_size

        except Exception as e:
            print(f"   ❌ Erreur: {e}")
            break

    print(f"\n✅ Total récupéré: {len(all_histories)} entrées\n")
    return all_histories


def parse_positions(histories: List[Dict]) -> Dict:
    """Parse l'historique et crée les positions"""

    print("📊 Parsing des positions...\n")

    positions = {}
    action_type_names = {
        'OPEN_MARKET': 'Open Market',
        'OPEN_LIMIT': 'Open Limit',
        'CANCEL_LIMIT': 'Cancel Limit',
        'EXECUTED_LIMIT': 'Executed Limit',
        'CLOSE_MARKET': 'Close Market',
        'CLOSE_TP': 'Close Take Profit',
        'CLOSE_SL': 'Close Stop Loss',
        'LIQUIDATE': 'Liquidation',
        'ADD_MARGIN': 'Add Margin',
        'UPDATE_TP_SL': 'Update TP/SL'
    }

    for entry in histories:
        # Chaque entry peut être soit un trade soit un limitOrder
        trade = entry.get('trade')
        limit_order = entry.get('limitOrder')

        if not trade and not limit_order:
            continue

        action_type = entry.get('actionType', 'UNKNOWN')
        timestamp = int(entry.get('timestamp', 0))
        tx_hash = entry.get('txHash', '')
        order_trade_hash = entry.get('orderOrTradeHash', '')

        # Timestamp ISO
        opened_at = datetime.fromtimestamp(timestamp).isoformat() if timestamp else None

        # Données du trade ou limit order
        data = trade if trade else limit_order
        pair_name = data.get('pair', {}).get('name', 'BTCUSD')
        is_long = data.get('isLong', False)
        token_symbol = data.get('token', {}).get('symbol', 'USDT')

        # Valeurs numériques (déjà en format décimal depuis The Graph!)
        if trade:
            # Trade exécuté
            margin = float(trade.get('margin', 0))
            entry_price = float(trade.get('entryPrice', 0))
            qty = float(trade.get('qty', 0))

            close_type = trade.get('closeType')
            close_price = float(trade.get('closePrice', 0)) if trade.get('closePrice') else None

            # PnL
            pnl = float(trade.get('actualPnl', 0)) if trade.get('actualPnl') else None

            # Fees
            open_fee = float(trade.get('openFee', 0))
            close_fee = float(trade.get('closeFee', 0))
            funding_fee = float(trade.get('fundingFee', 0))
            holding_fee = float(trade.get('holdingFee', 0))
            execution_fee = float(trade.get('executionFee', 0))

            # Leverage
            leverage = int((qty * entry_price) / margin) if margin > 0 and entry_price > 0 else 0

            # Status
            if close_type:
                if 'LIQUIDAT' in close_type.upper():
                    status = 'LIQUIDATED'
                else:
                    status = 'CLOSED'
                closed_at = opened_at
            else:
                status = 'OPEN'
                closed_at = None

            # PnL%
            pnl_pct = (pnl / margin) * 100 if pnl is not None and margin > 0 else None

            # TP/SL
            take_profit = 0
            stop_loss = 0

        else:
            # Limit order (pas encore exécuté)
            amount_in = float(limit_order.get('amountIn', 0))
            limit_price = float(limit_order.get('limitPrice', 0))
            qty = float(limit_order.get('qty', 0))
            take_profit = float(limit_order.get('takeProfit', 0))
            stop_loss = float(limit_order.get('stopLoss', 0))

            margin = amount_in
            entry_price = limit_price
            leverage = 0
            status = 'PENDING' if action_type == 'OPEN_LIMIT' else 'CANCELLED'
            closed_at = None
            close_price = None
            pnl = None
            pnl_pct = None
            open_fee = 0
            close_fee = 0
            funding_fee = 0
            holding_fee = 0
            execution_fee = 0

        # Trade hash unique
        trade_hash = order_trade_hash if order_trade_hash else tx_hash

        # Créer ou mettre à jour position
        position = {
            'trade_hash': trade_hash,
            'tx_hash': tx_hash,
            'symbol': f"{pair_name}_DEGEN",
            'pair_address': '0xD89aE81e55b1Bd46591CB5555209fdF8fFb43b76',  # Arbitrum
            'is_long': is_long,
            'margin': margin,
            'leverage': leverage,
            'entry_price': entry_price,
            'qty': qty,
            'stop_loss': stop_loss,
            'take_profit': take_profit,
            'opened_at': opened_at,
            'status': status,
            'closed_at': closed_at,
            'close_tx_hash': tx_hash if status != 'OPEN' else None,
            'exit_price': close_price,
            'pnl': pnl,
            'pnl_pct': pnl_pct,
            'close_reason': close_type,
            'open_fee': open_fee,
            'close_fee': close_fee,
            'funding_fee': funding_fee,
            'holding_fee': holding_fee,
            'execution_fee': execution_fee,
            'action_type': action_type,
            'token_symbol': token_symbol,
        }

        positions[trade_hash] = position

        # Afficher
        side = "LONG" if is_long else "SHORT"
        action_name = action_type_names.get(action_type, action_type)

        print(f"   {pair_name} {side} {leverage}x - {action_name} - {status}")
        print(f"      Margin: ${margin:.2f} | Entry: ${entry_price:,.2f}")
        if pnl is not None:
            print(f"      PnL: ${pnl:+.2f} ({pnl_pct:+.2f}%)")
        print()

    return positions


def save_positions(positions: Dict):
    """Sauvegarde dans positions.json"""

    data = {
        'positions': positions,
        'last_updated': datetime.now().isoformat(),
        'source': 'thegraph_arbitrum_gateway'
    }

    with open('positions.json', 'w') as f:
        json.dump(data, f, indent=2)

    print(f"✅ {len(positions)} positions sauvegardées dans positions.json\n")


def main():
    """Main"""

    # 1. Fetch depuis The Graph
    histories = fetch_all_history()

    if not histories:
        print("❌ Aucune donnée récupérée")
        return

    # 2. Parse
    positions = parse_positions(histories)

    if not positions:
        print("❌ Aucune position parsée")
        return

    # 3. Save
    save_positions(positions)

    # 4. Stats
    print("=" * 80)
    print("📊 RÉSUMÉ")
    print("=" * 80)

    open_positions = [p for p in positions.values() if p['status'] == 'OPEN']
    closed_positions = [p for p in positions.values() if p['status'] == 'CLOSED']
    liquidated = [p for p in positions.values() if p['status'] == 'LIQUIDATED']
    pending = [p for p in positions.values() if p['status'] == 'PENDING']
    cancelled = [p for p in positions.values() if p['status'] == 'CANCELLED']

    print(f"\nTotal positions: {len(positions)}")
    print(f"   OPEN: {len(open_positions)}")
    print(f"   CLOSED: {len(closed_positions)}")
    print(f"   LIQUIDATED: {len(liquidated)}")
    print(f"   PENDING: {len(pending)}")
    print(f"   CANCELLED: {len(cancelled)}")

    # Total PnL
    total_pnl = sum(p.get('pnl', 0) or 0 for p in positions.values() if p.get('pnl'))
    print(f"\n💰 Total PnL: ${total_pnl:+.2f}")

    # Total fees
    total_fees = sum(
        (p.get('open_fee', 0) or 0) +
        (p.get('close_fee', 0) or 0) +
        abs(p.get('funding_fee', 0) or 0) +
        (p.get('holding_fee', 0) or 0) +
        (p.get('execution_fee', 0) or 0)
        for p in positions.values()
    )
    print(f"💸 Total Fees: ${total_fees:.2f}")

    # Net PnL
    net_pnl = total_pnl - total_fees
    print(f"📈 Net PnL: ${net_pnl:+.2f}")

    print()
    print("✅ Dashboard: http://localhost:8889")


if __name__ == '__main__':
    main()
