#!/usr/bin/env python3
"""
Import historique des positions depuis CSV BSCScan
Reconstruit toutes les positions passées avec détails complets
"""

import csv
import io
import time
import requests
from datetime import datetime
from web3 import Web3, Account
from position_manager import PositionManager
import logging
import os
from dotenv import load_dotenv

logging.basicConfig(
    level=logging.INFO,
    format='%(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

load_dotenv()

# Config
BSC_RPC_URL = "https://bsc-dataseed.binance.org/"
BSCSCAN_API_KEY = os.getenv('BSCSCAN_API_KEY')
APOLLOX_CONTRACT = "0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0"

# Event signatures
OPEN_POSITION_EVENT = '0x0a6da83417411689fd88436e7fa57a7cf1cf635a35194c0658314d4a037382af'
CLOSE_POSITION_EVENT = '0x7064b82c073f138da0ec7646ebb51d4a0061d647accb4670bc564edf0bfac41d'

# CSV data from user
CSV_DATA = """Transaction Hash,Status,Method,BlockNo,DateTime (UTC),From,From_Nametag,To,To_Nametag,Amount,Value (USD),Token
"0x3f18f2e7535c8d1f7066c3a4e45b97edaff5deaaf10c1ead227323724265edc8","Success","Batch Request Price Callback","65512938","2025-10-22 12:18:10","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","51.25363472","$51.30","Binance-Peg BSC-USD (BSC-USD)"
"0x1343eeae0ecc648468836ab27bbf3aa4d4a501dc699035b013d9d98d72c30688","Success","Open Market Trade","65512928","2025-10-22 12:18:03","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","51.25363472","$51.30","Binance-Peg BSC-USD (BSC-USD)"
"0x772f3d07e541922ecdb6e9d6c9d7f59bbb171ac719a64ae1ec1c5f9ae287d2a9","Success","Execute Tp Sl Or Liq","65511134","2025-10-22 11:55:37","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","51.25363472","$51.30","Binance-Peg BSC-USD (BSC-USD)"
"0x684cfb0d231b1bb4a29763aaf51a7395a31d97ee1e2c89b4cc8513989b4bc398","Success","Open Market Trade","65511121","2025-10-22 11:55:28","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","55.49960501","$55.56","Binance-Peg BSC-USD (BSC-USD)"
"0xdf9a8c16420f16ae1deda37ce453d0df94342caef268d0693e49e806d35aa871","Success","Batch Request Price Callback","65511002","2025-10-22 11:53:58","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0xe7df22aa15b08b9c927d7ebc4f2ba9d79c33306c2c15dbaae955b1645c9a8d27","Success","Open Market Trade","65510993","2025-10-22 11:53:52","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0x15b80ae52bac80b5a40ba21f0dc76be94d3066bb603c0be0573b7d8dc187c449","Success","Batch Request Price Callback","65510959","2025-10-22 11:53:26","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0xab85960985a8eaa908bd64516d2c13e89a079d0144f4b55ae128fc7c146e57a9","Success","Open Market Trade","65510950","2025-10-22 11:53:19","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0x1e536bbfae4852e57d75514bfe8801efcae2d39dd5c31ba1f6f0053982f2d481","Success","Batch Request Price Callback","65510888","2025-10-22 11:52:33","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0x8dc2183fdb95d9153470362e293c5e49dc798c2c78b09e128cb25f5ddcdaca33","Success","Open Market Trade","65510879","2025-10-22 11:52:26","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0x66e5639f09ab1f8bba20d6fdfc9d2fd9cafb36116976ad8468edb5b3d6ba0b8f","Success","Batch Request Price Callback","65510815","2025-10-22 11:51:38","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0x25deac703103710b642129bdc6fced70bcf5d212767247215d0c1c82aba43a58","Success","Open Market Trade","65510806","2025-10-22 11:51:31","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0x0f504ffe6fdb30372553be6925af3cf736c578020522028a66453ea7b85dcdf6","Success","Batch Request Price Callback","65510787","2025-10-22 11:51:17","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0xe9e6ce88195a661f97cfb95a7c4699129b6cb2889179dee32181f3aa5a93fab3","Success","Open Market Trade","65510778","2025-10-22 11:51:10","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0xdd2073d302130d18122f490db128dcaa3a84d3b2c9dae981030d38f3c33496d9","Success","Batch Request Price Callback","65509523","2025-10-22 11:35:29","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0x59da5b5c41ecb0171c0be25a5e4436b1317487dec93a02b8a17340932c83ceab","Success","Open Market Trade","65509515","2025-10-22 11:35:23","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0xb1b7eb68b3e7d5a3a74ac5a301e817634551c753e890f6904a465f81af8033f6","Success","Batch Request Price Callback","65509057","2025-10-22 11:29:39","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0xfbfd35181fb34048d8e0f444b92354b90f4c97a46c0d6874be034551cd21ec10","Success","Open Market Trade","65509048","2025-10-22 11:29:32","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0x23c8c49d7603c6057f794dde22c6cf0c3acba77e5e932efd556cd6d13333fd32","Success","Batch Request Price Callback","65508848","2025-10-22 11:27:02","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"0xd89bd9da1b0487b91d7505eb8ec0db529e21e670efbb5a9687b26f3c1743d23f","Success","Open Market Trade","65508840","2025-10-22 11:26:56","0x7476737C49d55bea691e54c2c96fD39a6bAf3049","","0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0","Aster: Trading","25","$25.03","Binance-Peg BSC-USD (BSC-USD)"
"""


def get_tx_receipt_web3(w3, tx_hash):
    """Récupère le receipt d'une TX via Web3 RPC"""
    try:
        receipt = w3.eth.get_transaction_receipt(tx_hash)
        # Convertir en dict pour compatibilité
        return {
            'blockNumber': receipt['blockNumber'],
            'transactionHash': receipt['transactionHash'].hex(),
            'logs': [
                {
                    'address': log['address'],
                    'topics': [t.hex() for t in log['topics']],
                    'data': log['data'].hex() if isinstance(log['data'], bytes) else log['data']
                }
                for log in receipt['logs']
            ]
        }
    except Exception as e:
        logger.warning(f"Error fetching receipt via RPC: {e}")
        return None


def extract_position_from_receipt(receipt, margin_amount):
    """Extrait les détails de position depuis le receipt"""

    if not receipt or not receipt.get('logs'):
        return None

    # Chercher l'event OpenPosition
    for log in receipt['logs']:
        if log.get('address', '').lower() != APOLLOX_CONTRACT.lower():
            continue

        topics = log.get('topics', [])
        if len(topics) < 3:
            continue

        event_sig = topics[0]
        # Normaliser les signatures (enlever 0x si présent)
        event_sig_clean = event_sig.replace('0x', '').lower()
        target_sig_clean = OPEN_POSITION_EVENT.replace('0x', '').lower()

        if event_sig_clean != target_sig_clean:
            continue

        # Topic 1 = trade hash, Topic 2 = pair address
        trade_hash = topics[1]
        pair_address = '0x' + topics[2].replace('0x', '')[-40:]

        # Décoder les données (très simplifié, on a juste le margin du CSV)
        return {
            'trade_hash': trade_hash,
            'pair_address': pair_address,
            'margin': margin_amount
        }

    return None


def parse_csv_and_import():
    """Parse le CSV et importe toutes les positions"""

    # Initialiser Web3 et position manager
    w3 = Web3(Web3.HTTPProvider(BSC_RPC_URL))

    private_key = os.getenv('WALLET_PRIVATE_KEY')
    account = Account.from_key(private_key)
    manager = PositionManager(account.address, 'positions.json')

    # Parser le CSV
    reader = csv.DictReader(io.StringIO(CSV_DATA))
    transactions = list(reader)

    # Séparer open et close
    opens = [tx for tx in transactions if tx['Method'] == 'Open Market Trade']
    closes = [tx for tx in transactions if tx['Method'] in ['Batch Request Price Callback', 'Execute Tp Sl Or Liq']]

    logger.info(f"📊 Trouvé: {len(opens)} ouvertures, {len(closes)} fermetures")

    imported = 0
    skipped = 0

    for open_tx in opens:
        tx_hash = open_tx['Transaction Hash']
        margin = float(open_tx['Amount'])
        block_no = int(open_tx['BlockNo'])
        opened_at = open_tx['DateTime (UTC)']

        logger.info(f"\n🔍 Analyse: {tx_hash[:16]}... (${margin:.2f})")

        # Récupérer receipt via Web3 RPC
        receipt = get_tx_receipt_web3(w3, tx_hash)

        if not receipt:
            logger.warning(f"   ⏭️  Skipped: no receipt")
            skipped += 1
            continue

        # Extraire position details
        pos_details = extract_position_from_receipt(receipt, margin)

        if not pos_details:
            logger.warning(f"   ⏭️  Skipped: no position event")
            skipped += 1
            continue

        trade_hash = pos_details['trade_hash']

        # Vérifier si déjà importée
        if manager.get_position(trade_hash):
            logger.info(f"   ⏭️  Already imported")
            continue

        # Chercher la fermeture correspondante
        close_amount = None
        closed_at = None
        close_tx_hash = None

        for close_tx in closes:
            close_block = int(close_tx['BlockNo'])
            if close_block > block_no:  # Fermeture après ouverture
                close_amount = float(close_tx['Amount'])
                closed_at = close_tx['DateTime (UTC)']
                close_tx_hash = close_tx['Transaction Hash']
                break

        # Ajouter la position (directement via l'objet Position pour garder la date)
        try:
            from position_manager import Position

            position = Position(
                trade_hash=trade_hash,
                tx_hash=tx_hash,
                symbol='BTCUSD_DEGEN',
                pair_address=pos_details['pair_address'],
                is_long=True,
                margin=margin,
                leverage=200,
                entry_price=0,
                qty=0,
                stop_loss=0,
                take_profit=0,
                opened_at=opened_at,
                block_number=block_no,
                status='OPEN'
            )

            manager.positions[trade_hash] = position
            manager.save()

            # Si fermée, mettre à jour
            if closed_at:
                pnl = close_amount - margin if close_amount else 0
                manager.close_position(
                    trade_hash=trade_hash,
                    close_tx_hash=close_tx_hash or "0x0",
                    exit_price=0,  # Inconnu
                    pnl=pnl,
                    block_number=0
                )
                logger.info(f"   ✅ Imported: CLOSED (PnL: ${pnl:+.2f})")
            else:
                logger.info(f"   ✅ Imported: OPEN")

            imported += 1

        except Exception as e:
            logger.error(f"   ❌ Error: {e}")
            skipped += 1

        # Rate limit: max 5 calls/sec
        time.sleep(0.25)

    return imported, skipped


if __name__ == '__main__':
    print("=" * 80)
    print("📥 IMPORT HISTORIQUE DEPUIS CSV")
    print("=" * 80)
    print()

    if not BSCSCAN_API_KEY:
        print("❌ Erreur: BSCSCAN_API_KEY manquante dans .env")
        exit(1)

    imported, skipped = parse_csv_and_import()

    print()
    print("=" * 80)
    print(f"✅ IMPORT TERMINÉ")
    print("=" * 80)
    print(f"   Importées: {imported}")
    print(f"   Skipped: {skipped}")
    print()
    print("💡 Vérifiez: http://localhost:8889")
    print("=" * 80)
