#!/usr/bin/env python3
"""
Import REAL history from ApolloX contract via getOrderAndTradeHistoryV2
"""

from web3 import Web3
import json
from datetime import datetime
import os
from dotenv import load_dotenv

load_dotenv()

WALLET_ADDRESS = os.getenv('WALLET_ADDRESS', '0x7476737C49d55bea691e54c2c96fD39a6bAf3049')
APOLLOX_CONTRACT = "0x1b6F2d3844C6ae7D56ceb3C3643b9060ba28FEb0"
BSC_RPC = "https://bsc-dataseed.binance.org/"

w3 = Web3(Web3.HTTPProvider(BSC_RPC))

# ABI simplifiée pour getOrderAndTradeHistoryV2
ABI = [{
    "inputs": [
        {"internalType": "address", "name": "user", "type": "address"},
        {"internalType": "uint256", "name": "start", "type": "uint256"},
        {"internalType": "uint8", "name": "size", "type": "uint8"}
    ],
    "name": "getOrderAndTradeHistoryV2",
    "outputs": [{
        "components": [
            {"internalType": "bytes32", "name": "hash", "type": "bytes32"},
            {"internalType": "uint40", "name": "timestamp", "type": "uint40"},
            {"internalType": "string", "name": "pair", "type": "string"},
            {"internalType": "enum IOrderAndTradeHistory.ActionType", "name": "actionType", "type": "uint8"},
            {"internalType": "address", "name": "tokenIn", "type": "address"},
            {"internalType": "bool", "name": "isLong", "type": "bool"},
            {"internalType": "uint96", "name": "amountIn", "type": "uint96"},
            {"internalType": "uint80", "name": "qty", "type": "uint80"},
            {"internalType": "uint64", "name": "entryPrice", "type": "uint64"},
            {"internalType": "uint96", "name": "margin", "type": "uint96"},
            {"internalType": "uint96", "name": "openFee", "type": "uint96"},
            {"internalType": "uint96", "name": "executionFee", "type": "uint96"},
            {"internalType": "uint64", "name": "closePrice", "type": "uint64"},
            {"internalType": "int96", "name": "fundingFee", "type": "int96"},
            {"internalType": "uint96", "name": "closeFee", "type": "uint96"},
            {"internalType": "int96", "name": "pnl", "type": "int96"},
            {"internalType": "uint96", "name": "holdingFee", "type": "uint96"},
            {"internalType": "uint40", "name": "openTimestamp", "type": "uint40"}
        ],
        "internalType": "struct IOrderAndTradeHistory.OrderAndTradeHistory[]",
        "name": "datas",
        "type": "tuple[]"
    }],
    "stateMutability": "view",
    "type": "function"
}]


def fetch_history():
    """Récupère l'historique depuis le contrat"""

    print("=" * 80)
    print("📥 IMPORT HISTORIQUE DEPUIS APOLLOX CONTRACT")
    print("=" * 80)
    print()
    print(f"Wallet: {WALLET_ADDRESS}")
    print(f"Contract: {APOLLOX_CONTRACT}\n")

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

    all_history = []
    start = 0
    batch_size = 100  # Max par appel

    print("🔍 Récupération de l'historique...\n")

    while True:
        try:
            history_batch = contract.functions.getOrderAndTradeHistoryV2(
                Web3.to_checksum_address(WALLET_ADDRESS),
                start,
                batch_size
            ).call()

            if not history_batch:
                break

            print(f"   Batch {start // batch_size + 1}: {len(history_batch)} entrées")
            all_history.extend(history_batch)

            if len(history_batch) < batch_size:
                break

            start += batch_size

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

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


def parse_history(history):
    """Parse l'historique et crée positions.json"""

    positions = {}
    action_types = [
        "OPEN_MARKET",      # 0
        "OPEN_LIMIT",       # 1
        "CANCEL_LIMIT",     # 2
        "EXECUTED_LIMIT",   # 3
        "CLOSE_MARKET",     # 4
        "CLOSE_TP",         # 5
        "CLOSE_SL",         # 6
        "LIQUIDATE"         # 7
    ]

    print("📊 Parsing de l'historique...\n")

    # Group par trade hash
    trades = {}
    for entry in history:
        trade_hash = entry[0].hex()  # hash

        if trade_hash not in trades:
            trades[trade_hash] = []
        trades[trade_hash].append(entry)

    # Créer les positions
    for trade_hash, entries in trades.items():
        # Chercher l'ouverture
        open_entry = None
        close_entry = None

        for entry in entries:
            action_type = entry[3]  # actionType
            if action_type in [0, 1, 3]:  # OPEN ou EXECUTED_LIMIT
                open_entry = entry
            elif action_type in [4, 5, 6, 7]:  # CLOSE
                close_entry = entry

        if not open_entry:
            continue

        # Parse open
        timestamp = open_entry[1]  # timestamp
        pair = open_entry[2]  # pair
        action_type = open_entry[3]
        token_in = open_entry[4]
        is_long = open_entry[5]
        amount_in = open_entry[6] / 1e18
        qty = open_entry[7] / 1e10
        entry_price = open_entry[8] / 1e8
        margin = open_entry[9] / 1e18
        open_fee = open_entry[10] / 1e18
        execution_fee = open_entry[11] / 1e18

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

        # Timestamp
        opened_at = datetime.fromtimestamp(timestamp).isoformat()

        # Status et close data
        if close_entry:
            close_timestamp = close_entry[1]
            close_action = close_entry[3]
            close_price = close_entry[12] / 1e8
            funding_fee = close_entry[13] / 1e18
            close_fee = close_entry[14] / 1e18
            pnl = close_entry[15] / 1e18
            holding_fee = close_entry[16] / 1e18

            closed_at = datetime.fromtimestamp(close_timestamp).isoformat()

            # Déterminer status
            if close_action == 7:  # LIQUIDATE
                status = 'LIQUIDATED'
                close_reason = 'LIQUIDATION'
            elif close_action == 5:  # CLOSE_TP
                status = 'CLOSED'
                close_reason = 'TAKE_PROFIT'
            elif close_action == 6:  # CLOSE_SL
                status = 'CLOSED'
                close_reason = 'STOP_LOSS'
            else:  # CLOSE_MARKET
                status = 'CLOSED'
                close_reason = 'MANUAL'

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

        else:
            status = 'OPEN'
            closed_at = None
            close_price = None
            funding_fee = 0
            close_fee = 0
            pnl = None
            holding_fee = 0
            close_reason = None
            pnl_pct = None

        # Créer position
        position = {
            'trade_hash': trade_hash,
            'tx_hash': trade_hash,  # Utiliser trade_hash comme fallback
            'symbol': f"{pair}_DEGEN",
            'pair_address': '0xD89aE81e55b1Bd46591CB5555209fdF8fFb43b76',
            'is_long': is_long,
            'margin': margin,
            'leverage': leverage,
            'entry_price': entry_price,
            'qty': qty,
            'stop_loss': 0,
            'take_profit': 0,
            'opened_at': opened_at,
            'status': status,
            'closed_at': closed_at,
            'close_tx_hash': None,
            'exit_price': close_price,
            'pnl': pnl,
            'pnl_pct': pnl_pct,
            'close_reason': close_reason,
            'open_fee': open_fee,
            'close_fee': close_fee,
            'funding_fee': funding_fee,
            'holding_fee': holding_fee,
            'execution_fee': execution_fee,
            'token_in': token_in
        }

        positions[trade_hash] = position

        # Afficher
        side = "LONG" if is_long else "SHORT"
        print(f"   {pair} {side} {leverage}x - {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):
    """Sauvegarde dans positions.json"""

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

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

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


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

    # 1. Fetch
    history = fetch_history()

    if not history:
        print("❌ Aucun historique récupéré")
        return

    # 2. Parse
    positions = parse_history(history)

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

    # 3. Save
    save_positions(positions)

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

    open_pos = [p for p in positions.values() if p['status'] == 'OPEN']
    closed_pos = [p for p in positions.values() if p['status'] == 'CLOSED']
    liquidated = [p for p in positions.values() if p['status'] == 'LIQUIDATED']

    print(f"Total positions: {len(positions)}")
    print(f"   OPEN: {len(open_pos)}")
    print(f"   CLOSED: {len(closed_pos)}")
    print(f"   LIQUIDATED: {len(liquidated)}")

    # Total PnL
    total_pnl = sum(p.get('pnl', 0) or 0 for p in positions.values())
    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)
        for p in positions.values()
    )
    print(f"💸 Total Fees: ${total_fees:.2f}")

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


if __name__ == '__main__':
    main()
