#!/usr/bin/env python3
"""
Enrichit les positions existantes en décodant les transactions blockchain
Récupère entry_price, exit_price, qty, PnL depuis les TX réelles
"""

import json
import os
from web3 import Web3
from eth_abi import decode
import requests
from datetime import datetime
from dotenv import load_dotenv

load_dotenv()

# Config
BSC_RPC = "https://bsc-dataseed.binance.org/"
BSCSCAN_API_KEY = os.getenv('BSCSCAN_API_KEY', 'YourApiKeyToken')
APOLLOX_CONTRACT = "0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0"
POSITIONS_FILE = 'positions.json'

w3 = Web3(Web3.HTTPProvider(BSC_RPC))

# ABI simplifié ApolloX (juste les fonctions qui nous intéressent)
APOLLOX_ABI = [
    {
        "inputs": [
            {"name": "pairIndex", "type": "uint256"},
            {"name": "price", "type": "uint256"},
            {"name": "tp", "type": "uint256"},
            {"name": "sl", "type": "uint256"},
            {"name": "long", "type": "bool"},
            {"name": "leverage", "type": "uint256"},
            {"name": "referral", "type": "address"}
        ],
        "name": "openMarketTrade",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {"name": "pairIndex", "type": "uint256"}
        ],
        "name": "closeTrade",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    }
]

contract = w3.eth.contract(address=Web3.to_checksum_address(APOLLOX_CONTRACT), abi=APOLLOX_ABI)


def get_btc_price_at_timestamp(timestamp):
    """Récupère le prix BTC depuis Binance à un timestamp donné (approximatif)"""
    try:
        # Convertir timestamp ISO en unix
        if isinstance(timestamp, str):
            dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
            unix_ts = int(dt.timestamp() * 1000)
        else:
            unix_ts = int(timestamp * 1000)

        # Binance Klines API (5m candles)
        url = f"https://api.binance.com/api/v3/klines"
        params = {
            'symbol': 'BTCUSDT',
            'interval': '5m',
            'startTime': unix_ts - 300000,  # -5min
            'endTime': unix_ts + 300000,    # +5min
            'limit': 1
        }

        response = requests.get(url, params=params, timeout=5)
        response.raise_for_status()
        data = response.json()

        if data:
            # Retourner le prix d'ouverture de la bougie
            return float(data[0][1])

        return None

    except Exception as e:
        print(f"   ⚠️  Erreur récupération prix: {e}")
        return None


def decode_open_trade_tx(tx_hash):
    """Décode une transaction openMarketTrade"""
    try:
        # Récupérer la transaction
        tx = w3.eth.get_transaction(tx_hash)
        receipt = w3.eth.get_transaction_receipt(tx_hash)

        if not tx or not receipt:
            return None

        # Décoder l'input - Handle HexBytes type from web3.py
        input_data = tx['input']

        # Convert to hex string if bytes
        if isinstance(input_data, bytes):
            input_hex = input_data.hex()
        else:
            input_hex = input_data

        # Les 4 premiers bytes = function selector
        func_selector = input_hex[:10]
        params_hex = input_hex[10:]

        # Remove 0x prefix if present
        if params_hex.startswith('0x'):
            params_hex = params_hex[2:]

        # Décoder les paramètres selon l'ABI
        # openMarketTrade(uint256,uint256,uint256,uint256,bool,uint256,address)
        decoded = decode(
            ['uint256', 'uint256', 'uint256', 'uint256', 'bool', 'uint256', 'address'],
            bytes.fromhex(params_hex)
        )

        pair_index, price, tp, sl, is_long, leverage, referral = decoded

        # Margin = value envoyé dans la TX
        margin = float(w3.from_wei(tx['value'], 'ether'))

        # Convertir les prix (ApolloX utilise souvent 1e10 comme précision)
        entry_price = price / 1e10 if price > 0 else 0
        tp_price = tp / 1e10 if tp > 0 else 0
        sl_price = sl / 1e10 if sl > 0 else 0

        # Calculer qty
        qty = (margin * leverage) / entry_price if entry_price > 0 else 0

        result = {
            'entry_price': entry_price,
            'qty': qty,
            'stop_loss': sl_price,
            'take_profit': tp_price,
            'is_long': is_long,
            'leverage': int(leverage),
            'margin': margin,
            'gas_used': receipt['gasUsed']
        }

        return result

    except Exception as e:
        print(f"      ⚠️  Erreur décodage TX: {e}")
        return None


def enrich_position(position):
    """Enrichit une position avec les données blockchain"""
    tx_hash = position.get('tx_hash')

    if not tx_hash:
        return False

    print(f"\n   🔍 {position.get('symbol', 'UNKNOWN')} - {tx_hash[:16]}...")

    # 1. Décoder la transaction d'ouverture
    open_data = decode_open_trade_tx(tx_hash)

    if not open_data:
        print(f"      ❌ Impossible de décoder la TX d'ouverture")
        return False

    # 2. Mettre à jour les données d'ouverture
    position['entry_price'] = open_data['entry_price']
    position['qty'] = open_data['qty']
    position['stop_loss'] = open_data['stop_loss']
    position['take_profit'] = open_data['take_profit']
    position['leverage'] = open_data['leverage']

    print(f"      ✅ Entry: ${open_data['entry_price']:.2f} | Qty: {open_data['qty']:.4f} | Lev: {open_data['leverage']}x")

    # 3. Si position fermée, récupérer le prix de sortie
    if position.get('status') in ['CLOSED', 'LIQUIDATED']:
        # Option A: Utiliser le prix de la bougie au moment de la fermeture
        closed_at = position.get('closed_at')

        if closed_at:
            exit_price = get_btc_price_at_timestamp(closed_at)

            if exit_price:
                position['exit_price'] = exit_price

                # Calculer PnL
                entry = position['entry_price']
                is_long = position.get('is_long', True)
                margin = position.get('margin', 0)
                leverage = position.get('leverage', 1)

                if is_long:
                    pnl_pct = (exit_price - entry) / entry
                else:
                    pnl_pct = (entry - exit_price) / entry

                pnl_usd = pnl_pct * margin * leverage

                position['pnl'] = round(pnl_usd, 2)
                position['pnl_pct'] = round(pnl_pct * 100, 2)

                print(f"      💰 Exit: ${exit_price:.2f} | PnL: ${pnl_usd:+.2f} ({pnl_pct*100:+.2f}%)")

    return True


def main():
    """Enrichit toutes les positions depuis positions.json"""

    print("=" * 80)
    print("🔬 ENRICHISSEMENT POSITIONS DEPUIS BLOCKCHAIN")
    print("=" * 80)
    print()

    # Charger positions.json
    if not os.path.exists(POSITIONS_FILE):
        print(f"❌ Fichier {POSITIONS_FILE} non trouvé")
        return

    with open(POSITIONS_FILE, 'r') as f:
        data = json.load(f)

    positions = data.get('positions', {})

    print(f"📂 {len(positions)} positions trouvées")
    print()

    # Enrichir chaque position
    enriched_count = 0

    for trade_hash, position in positions.items():
        # Skip si déjà enrichie
        if position.get('entry_price', 0) > 0:
            print(f"\n   ⏭️  {position.get('symbol', 'UNKNOWN')} - Déjà enrichie")
            continue

        if enrich_position(position):
            enriched_count += 1

    # Sauvegarder
    if enriched_count > 0:
        data['last_updated'] = datetime.now().isoformat()

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

        print()
        print("=" * 80)
        print(f"✅ {enriched_count} positions enrichies")
        print("=" * 80)
        print()
        print("💡 Dashboard: http://localhost:8889")
    else:
        print()
        print("ℹ️  Aucune position à enrichir (toutes déjà complètes)")


if __name__ == '__main__':
    main()
