"""
Web Monitor SIMPLE pour Apollo Degen Bot
"""

from flask import Flask, render_template, jsonify
import json
from pathlib import Path
from datetime import datetime
import os
from dotenv import load_dotenv
import requests
from thegraph_client import TheGraphClient
import time

load_dotenv()

app = Flask(__name__)
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
app.config['TEMPLATES_AUTO_RELOAD'] = True

POSITIONS_FILE = Path('positions.json')
USDT_CONTRACT = '0x55d398326f99059fF775485246999027B3197955'
BSC_RPC = 'https://bsc-dataseed.binance.org/'

# Cache The Graph (60 secondes)
THEGRAPH_CACHE = {
    'positions': None,
    'timestamp': 0,
    'ttl': 60  # secondes
}

# Client The Graph
WALLET_ADDRESS = os.getenv('WALLET_ADDRESS', '0x7476737C49d55bea691e54c2c96fD39a6bAf3049')
thegraph_client = TheGraphClient(WALLET_ADDRESS)


def _merge_with_local_positions(thegraph_positions):
    """Merge positions from The Graph with local bot positions"""
    try:
        # Utiliser chemin absolu pour positions.json
        local_file = Path('/var/www/html/HyperOracle/crypto_trader/apollo_degen_bot/positions.json')

        print(f"🔍 Checking local positions file: {local_file}", flush=True)

        if not local_file.exists():
            print(f"⚠️  Local positions file not found: {local_file}", flush=True)
            return thegraph_positions

        with open(local_file, 'r') as f:
            local_data = json.load(f)

        local_positions = local_data.get('positions', {})
        print(f"📂 Local positions loaded: {len(local_positions)} positions", flush=True)

        # Convertir dict en liste si nécessaire
        if isinstance(local_positions, dict):
            local_positions = list(local_positions.values())

        # Merger: positions locales ont priorité (plus récentes)
        merged = {}

        # D'abord ajouter positions The Graph
        for pos in thegraph_positions:
            trade_hash = pos.get('trade_hash')
            if trade_hash:
                merged[trade_hash] = pos

        # Puis ajouter/overwrite avec positions locales
        for pos in local_positions:
            trade_hash = pos.get('trade_hash')
            if trade_hash:
                merged[trade_hash] = pos

        print(f"🔄 Merged: {len(thegraph_positions)} from Graph + {len(local_positions)} local = {len(merged)} total", flush=True)
        return list(merged.values())

    except Exception as e:
        print(f"⚠️  Erreur merge positions locales: {e}", flush=True)
        return thegraph_positions


@app.route('/')
def index():
    """Page principale"""
    response = render_template('monitor.html')
    return response, 200, {
        'Cache-Control': 'no-cache, no-store, must-revalidate',
        'Pragma': 'no-cache',
        'Expires': '0'
    }


@app.route('/api/status')
def api_status():
    """Status basique avec valeur totale du compte"""
    try:
        stats = get_stats_from_file()

        # Try to get wallet balances, fallback to 0 if error
        try:
            wallet = get_wallet_balances_simple()
        except Exception as e:
            print(f"⚠️  Wallet balance error (using fallback): {e}")
            import traceback
            traceback.print_exc()
            wallet = {'bnb': 0, 'usdt': 0, 'address': 'N/A'}

        # Calculer la valeur totale du compte (libre + margin + PnL)
        positions = load_positions()
        open_positions = [p for p in positions if p.get('status') == 'OPEN']

        total_margin = 0
        total_pnl = 0
        total_fees = 0

        # Calculer margin et PnL pour chaque position ouverte
        current_price = get_current_btc_price()
        for pos in open_positions:
            margin = pos.get('margin', 0)
            total_margin += margin

            # Calculer les fees déjà payées
            fees_paid = (
                pos.get('open_fee', 0) +
                abs(pos.get('funding_fee', 0)) +
                pos.get('holding_fee', 0) +
                pos.get('execution_fee', 0)
            )
            total_fees += fees_paid

            # Calculer PnL si on a le prix actuel
            if current_price and pos.get('entry_price'):
                entry = pos['entry_price']
                is_long = pos.get('is_long', True)
                leverage = pos.get('leverage', 1)

                if is_long:
                    pnl_pct = (current_price - entry) / entry
                else:  # SHORT
                    pnl_pct = (entry - current_price) / entry

                pnl_usd = pnl_pct * leverage * margin
                total_pnl += pnl_usd

        # Valeur totale = USDT libre + BNB en USD + Margin + PnL net
        usdt_free = wallet.get('usdt', 0)
        bnb_usd = wallet.get('bnb', 0) * 600  # Approximation BNB -> USD
        total_pnl_net = total_pnl - total_fees
        total_value = usdt_free + bnb_usd + total_margin + total_pnl_net

        wallet['total_value'] = round(total_value, 2)
        wallet['margin_in_positions'] = round(total_margin, 2)
        wallet['unrealized_pnl'] = round(total_pnl, 2)
        wallet['unrealized_pnl_net'] = round(total_pnl_net, 2)
        wallet['fees_paid'] = round(total_fees, 2)

        return jsonify({
            'bot_running': True,
            'timestamp': datetime.now().isoformat(),
            'wallet': wallet,
            'positions': {},
            'stats': stats
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500


@app.route('/api/positions/open')
def api_positions_open():
    """Positions ouvertes avec PnL en temps réel"""
    try:
        positions = load_positions()
        open_positions = [p for p in positions if p.get('status') == 'OPEN']

        # Calculer PnL en temps réel pour chaque position
        current_price = get_current_btc_price()
        for pos in open_positions:
            if current_price and pos.get('entry_price'):
                entry = pos['entry_price']
                is_long = pos.get('is_long', True)
                margin = pos.get('margin', 0)
                leverage = pos.get('leverage', 1)

                # Calculer PnL
                if is_long:
                    pnl_pct = (current_price - entry) / entry
                else:  # SHORT
                    pnl_pct = (entry - current_price) / entry

                pnl_usd = pnl_pct * leverage * margin

                # Calculer les fees déjà payées (sans close_fee car position encore ouverte)
                fees_paid = (
                    pos.get('open_fee', 0) +
                    abs(pos.get('funding_fee', 0)) +
                    pos.get('holding_fee', 0) +
                    pos.get('execution_fee', 0)
                )

                # PnL net = PnL - fees déjà payées
                pnl_net_usd = pnl_usd - fees_paid

                # Ajouter au dict
                pos['current_price'] = round(current_price, 2)
                pos['pnl'] = round(pnl_usd, 2)
                pos['pnl_pct'] = round(pnl_pct * 100, 2)
                pos['pnl_net'] = round(pnl_net_usd, 2)
                pos['pnl_net_pct'] = round((pnl_net_usd / margin) * 100, 2) if margin > 0 else 0
                pos['fees_paid'] = round(fees_paid, 2)

        return jsonify({
            'open_positions': open_positions,
            'count': len(open_positions),
            'timestamp': datetime.now().isoformat(),
            'btc_price': current_price
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500


@app.route('/api/positions/closed')
def api_positions_closed():
    """Positions fermées (plus récentes en haut)"""
    try:
        positions = load_positions()
        closed_positions = [p for p in positions if p.get('status') == 'CLOSED']

        # Trier par date de fermeture (plus récentes en premier)
        closed_positions.sort(key=lambda x: x.get('closed_at', ''), reverse=True)

        return jsonify({
            'closed_positions': closed_positions,
            'count': len(closed_positions),
            'timestamp': datetime.now().isoformat()
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500


@app.route('/api/positions')
def api_positions():
    """Toutes les positions"""
    try:
        positions = load_positions()
        stats = get_stats_from_file()

        return jsonify({
            'positions': positions,
            'stats': stats,
            'timestamp': datetime.now().isoformat()
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500


@app.route('/api/logs')
def api_logs():
    """Recent bot logs"""
    try:
        log_file = Path('bot_v10_onchain.log')
        if not log_file.exists():
            return jsonify({
                'logs': [],
                'count': 0,
                'timestamp': datetime.now().isoformat()
            })

        # Lire les dernières 50 lignes
        with open(log_file, 'r') as f:
            lines = f.readlines()
            recent_logs = lines[-50:] if len(lines) > 50 else lines

        # Parser les logs
        parsed_logs = []
        for line in recent_logs:
            line = line.strip()
            if not line:
                continue

            # Détecter le type de log
            log_type = 'info'
            if 'ERROR' in line or '❌' in line:
                log_type = 'error'
            elif 'WARNING' in line or '⚠️' in line:
                log_type = 'warning'
            elif '✅' in line or '🎯' in line or '💰' in line:
                log_type = 'success'

            parsed_logs.append({
                'message': line,
                'type': log_type
            })

        return jsonify({
            'logs': parsed_logs,
            'count': len(parsed_logs),
            'timestamp': datetime.now().isoformat()
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500


@app.route('/api/debug/balance')
def api_debug_balance():
    """Debug balance fetching"""
    try:
        private_key = os.getenv('WALLET_PRIVATE_KEY')
        has_key = private_key is not None and len(private_key) > 0

        result = get_wallet_balances_simple()

        return jsonify({
            'has_private_key': has_key,
            'balance_result': result,
            'timestamp': datetime.now().isoformat()
        })
    except Exception as e:
        import traceback
        return jsonify({
            'error': str(e),
            'traceback': traceback.format_exc()
        }), 500


@app.route('/api/positions/close/<symbol>', methods=['POST'])
def api_close_position(symbol):
    """Demande la fermeture d'une position"""
    try:
        # Create a close signal file for the bot to pick up
        close_signal_file = Path('close_position_signal.json')

        close_signal = {
            'symbol': symbol,
            'timestamp': datetime.now().isoformat(),
            'action': 'CLOSE'
        }

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

        return jsonify({
            'status': 'success',
            'message': f'Signal de fermeture envoyé pour {symbol}',
            'symbol': symbol,
            'timestamp': datetime.now().isoformat()
        })

    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': str(e)
        }), 500


def load_positions():
    """Charge les positions depuis The Graph + positions locales du bot"""
    global THEGRAPH_CACHE

    # Vérifier le cache
    now = time.time()
    if (THEGRAPH_CACHE['positions'] is not None and
        now - THEGRAPH_CACHE['timestamp'] < THEGRAPH_CACHE['ttl']):
        # Cache valide, merger avec positions locales
        return _merge_with_local_positions(THEGRAPH_CACHE['positions'])

    # Cache expiré ou vide, interroger The Graph
    try:
        print(f"🔄 Rafraîchissement depuis The Graph...")
        positions = thegraph_client.fetch_positions(limit=1000)

        # Mettre à jour le cache
        THEGRAPH_CACHE['positions'] = positions
        THEGRAPH_CACHE['timestamp'] = now

        print(f"✅ {len(positions)} positions chargées depuis The Graph")

        # Merger avec positions locales (bot live)
        return _merge_with_local_positions(positions)

    except Exception as e:
        print(f"⚠️  Erreur The Graph, fallback vers positions.json: {e}")

        # Fallback: lire depuis positions.json
        if not POSITIONS_FILE.exists():
            return []

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

            positions = []
            for trade_hash, pos_dict in data.get('positions', {}).items():
                pos_dict['trade_hash'] = trade_hash
                positions.append(pos_dict)

            return positions
        except Exception as e2:
            print(f"❌ Error loading positions from file: {e2}")
            return []


def get_stats_from_file():
    """Calculate des stats depuis positions.json"""
    positions = load_positions()
    closed = [p for p in positions if p.get('status') == 'CLOSED']

    if not closed:
        return {
            'closed_positions': 0,
            'winning_trades': 0,
            'losing_trades': 0,
            'win_rate': 0.0,
            'total_pnl': 0.0,
            'total_pnl_net': 0.0,
            'total_fees': 0.0
        }

    # Utiliser pnl_net si disponible, sinon pnl brut
    winning = [p for p in closed if p.get('pnl_net', p.get('pnl', 0)) > 0]
    losing = [p for p in closed if p.get('pnl_net', p.get('pnl', 0)) < 0]
    total_pnl = sum(p.get('pnl', 0) for p in closed)
    total_pnl_net = sum(p.get('pnl_net', 0) for p in closed)
    total_fees = sum(p.get('total_fees', 0) for p in closed)
    win_rate = (len(winning) / len(closed) * 100) if closed else 0

    return {
        'closed_positions': len(closed),
        'winning_trades': len(winning),
        'losing_trades': len(losing),
        'win_rate': round(win_rate, 1),
        'total_pnl': round(total_pnl, 2),
        'total_pnl_net': round(total_pnl_net, 2),
        'total_fees': round(total_fees, 2)
    }


def get_current_btc_price():
    """Récupère le prix actuel de BTC depuis Binance"""
    try:
        url = "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT"
        response = requests.get(url, timeout=3)
        response.raise_for_status()
        data = response.json()
        return float(data['price'])
    except Exception as e:
        print(f"Error fetching BTC price: {e}")
        return None


def get_wallet_balances_simple():
    """Récupère les balances avec balanceOf simple"""
    try:
        from web3 import Web3
        from web3 import Account

        private_key = os.getenv('WALLET_PRIVATE_KEY')
        if not private_key:
            return {'bnb': 0, 'usdt': 0, 'address': 'N/A'}

        account = Account.from_key(private_key)
        w3 = Web3(Web3.HTTPProvider(BSC_RPC, request_kwargs={'timeout': 5}))

        # BNB balance (native)
        bnb_wei = w3.eth.get_balance(account.address)
        bnb_balance = float(w3.from_wei(bnb_wei, 'ether'))

        # USDT balance (ERC20)
        usdt_abi = [{"constant":True,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"}]
        usdt_contract = w3.eth.contract(address=Web3.to_checksum_address(USDT_CONTRACT), abi=usdt_abi)
        usdt_balance_wei = usdt_contract.functions.balanceOf(account.address).call()
        usdt_balance = float(usdt_balance_wei / 10**18)

        return {
            'bnb': round(bnb_balance, 6),
            'usdt': round(usdt_balance, 2),
            'address': account.address
        }
    except Exception as e:
        print(f"Error fetching balances: {e}")
        return {'bnb': 0, 'usdt': 0, 'address': 'N/A', 'error': str(e)}


if __name__ == '__main__':
    Path('templates').mkdir(exist_ok=True)

    print("="*80)
    print("🌐 WEB MONITOR SIMPLE - APOLLO DEGEN BOT")
    print("="*80)
    print(f"\n📊 Interface: http://localhost:8889")
    print(f"   (ou https://degen.codeben.fr/)")
    print("\n🔄 Aucun cache - données temps réel")
    print("\nCtrl+C pour arrêter\n")

    app.jinja_env.auto_reload = True
    app.run(host='0.0.0.0', port=8889, debug=False)
