#!/usr/bin/env python3
"""
Grok-Integrated Crypto Trading Bot v1.0
Paper trading mode with xAI Grok API for AI-driven decisions
Integrates with NEOG Matrix Portal via WebSocket server

Author: NEOG System
Created: 2025-01-25
"""

import time
import os
import sys
import requests
import json
import sqlite3
from datetime import datetime
from pycoingecko import CoinGeckoAPI
from typing import Dict, List, Tuple, Optional

# ========== CONFIGURATION ==========

# Coins to track (AI tokens)
COINS = ['grok', 'bittensor', 'fetch-ai']  # $GROK (ERC), $TAO, $FET
VS_CURRENCY = 'usd'
CHECK_INTERVAL = int(os.getenv('CRYPTO_CHECK_INTERVAL', '60'))  # seconds
GROK_ANALYZE_THRESHOLD = float(os.getenv('CRYPTO_GROK_THRESHOLD', '0.02'))  # 2%

# Portfolio
INITIAL_CASH = float(os.getenv('CRYPTO_INITIAL_CASH', '140000'))  # R$
BRL_USD_RATE = 5.5  # Approximate, update if needed
MONTHLY_BURN = 8000  # R$ per month

# xAI API
XAI_API_KEY = os.getenv('XAI_API_KEY')
if not XAI_API_KEY:
    print("❌ ERROR: XAI_API_KEY not set. Get one at https://console.x.ai")
    print("   Export it: export XAI_API_KEY='your_key_here'")
    sys.exit(1)

GROK_URL = "https://api.x.ai/v1/chat/completions"
GROK_HEADERS = {
    "Authorization": f"Bearer {XAI_API_KEY}",
    "Content-Type": "application/json"
}

# Database
DB_PATH = '/Users/neog/crypto_trades.db'
LOG_FILE = '/Users/neog/crypto-grok.log'

# ========== GLOBALS ==========

cg = CoinGeckoAPI()
portfolio = {
    'cash': INITIAL_CASH,
    'holdings': {coin: 0.0 for coin in COINS}
}
db_conn = None

# ========== DATABASE SETUP ==========

def init_database():
    """Initialize SQLite database for trade history"""
    global db_conn
    db_conn = sqlite3.connect(DB_PATH, check_same_thread=False)
    cursor = db_conn.cursor()

    # Trades table
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS trades (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            coin TEXT NOT NULL,
            action TEXT NOT NULL,
            price_usd REAL NOT NULL,
            amount_brl REAL NOT NULL,
            units REAL NOT NULL,
            grok_reason TEXT,
            portfolio_value_brl REAL,
            runway_months REAL
        )
    ''')

    # Price history table
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS price_history (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            coin TEXT NOT NULL,
            price_usd REAL NOT NULL,
            change_1h REAL NOT NULL
        )
    ''')

    # Portfolio snapshots
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS portfolio_snapshots (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            cash_brl REAL NOT NULL,
            total_value_brl REAL NOT NULL,
            runway_months REAL NOT NULL,
            holdings_json TEXT NOT NULL
        )
    ''')

    db_conn.commit()
    log_message('info', 'Database initialized', {'db_path': DB_PATH})

# ========== LOGGING ==========

def log_message(level: str, message: str, data: Optional[Dict] = None):
    """Log to file and optionally to NEOG OS logging system"""
    timestamp = datetime.now().isoformat()
    log_entry = {
        'timestamp': timestamp,
        'level': level.upper(),
        'message': message,
        'data': data or {}
    }

    # File logging
    with open(LOG_FILE, 'a') as f:
        f.write(json.dumps(log_entry) + '\n')

    # Console
    emoji = {
        'info': 'ℹ️',
        'success': '✅',
        'warning': '⚠️',
        'error': '❌',
        'trade': '🚨'
    }.get(level, 'ℹ️')

    print(f"{emoji} [{timestamp}] {message}")
    if data:
        print(f"   Data: {json.dumps(data, indent=2)}")

    # TODO: Send to NEOG OS logging system (port 9001)
    # try:
    #     requests.post('http://localhost:9001/api/log', json={
    #         'level': level,
    #         'source': 'crypto-grok-bot',
    #         'message': message,
    #         'metadata': data
    #     }, timeout=2)
    # except:
    #     pass  # Don't fail if logging server is down

# ========== PRICE FETCHING ==========

def fetch_prices() -> Dict[str, Dict]:
    """
    Fetch current prices and 1h % change from CoinGecko
    Returns: {coin: {'price': float, 'change_1h': float}}
    """
    try:
        markets = cg.get_coins_markets(
            vs_currency=VS_CURRENCY,
            ids=','.join(COINS),
            price_change_percentage='1h',
            per_page=len(COINS),
            order='market_cap_desc'
        )

        data = {}
        for market in markets:
            coin_id = market['id']
            current = market.get('current_price', 0) or 0
            change_1h = (
                market.get('price_change_percentage_1h_in_currency', 0) or
                market.get('price_change_percentage_1h', 0) or
                0
            )
            data[coin_id] = {
                'price': float(current),
                'change_1h': float(change_1h)
            }

            # Save to price history
            cursor = db_conn.cursor()
            cursor.execute('''
                INSERT INTO price_history (timestamp, coin, price_usd, change_1h)
                VALUES (?, ?, ?, ?)
            ''', (datetime.now().isoformat(), coin_id, current, change_1h))
            db_conn.commit()

        log_message('info', 'Prices fetched', {'coins': len(data)})
        return data

    except Exception as e:
        log_message('error', f'Failed to fetch prices: {e}')
        return {}

# ========== GROK AI ANALYSIS ==========

def grok_analyze(coin: str, price: float, change: float) -> Tuple[str, str]:
    """
    Query Grok for trading recommendation
    Returns: (action, reason) where action in ['BUY', 'SELL', 'HOLD']
    """
    prompt = f"""Analyze: {coin.upper()} at ${price:.4f} USD, {change:+.2f}% 1h change.
Portfolio context: R$140K in AI tokens (GROK, TAO, FET).
Current holdings: {portfolio['holdings'][coin]:.4f} units worth R${portfolio['holdings'][coin] * price * BRL_USD_RATE:,.0f}.
Cash available: R${portfolio['cash']:,.0f}.

Recommend: BUY (strong uptrend + good entry), SELL (high risk/overvalued), or HOLD (wait).
Be CONSERVATIVE - only BUY on clear momentum, SELL on major risks.

Respond ONLY with 'BUY', 'SELL', or 'HOLD' as first word, then 1-sentence reason."""

    payload = {
        "model": "grok-2-1212",
        "messages": [
            {
                "role": "system",
                "content": "You are a crypto trading AI for an R$140K AI-token portfolio. Be conservative and data-driven."
            },
            {
                "role": "user",
                "content": prompt
            }
        ],
        "temperature": 0.1,  # Low for consistent decisions
        "max_tokens": 100
    }

    try:
        time.sleep(1)  # Rate limit buffer
        response = requests.post(GROK_URL, headers=GROK_HEADERS, json=payload, timeout=30)
        response.raise_for_status()

        result = response.json()
        content = result["choices"][0]["message"]["content"].strip()

        # Parse: first word = action, rest = reason
        words = content.split()
        action = words[0].upper()
        reason = ' '.join(words[1:]) if len(words) > 1 else "No reason provided"

        if action not in ['BUY', 'SELL', 'HOLD']:
            log_message('warning', f'Grok returned invalid action: {action}, defaulting to HOLD')
            action = 'HOLD'
            reason = f"Invalid response: {content}"

        log_message('success', f'Grok analyzed {coin}', {'action': action, 'reason': reason})
        return action, reason

    except requests.exceptions.Timeout:
        log_message('error', f'Grok API timeout for {coin}')
        return 'HOLD', 'API timeout'
    except requests.exceptions.RequestException as e:
        log_message('error', f'Grok API error for {coin}: {e}')
        return 'HOLD', f'API error: {str(e)[:50]}'
    except Exception as e:
        log_message('error', f'Grok analysis failed for {coin}: {e}')
        return 'HOLD', f'Analysis failed: {str(e)[:50]}'

# ========== TRADING LOGIC ==========

def calculate_portfolio_value(current_prices: Dict[str, Dict]) -> float:
    """Calculate total portfolio value in BRL"""
    total = portfolio['cash']
    for coin, units in portfolio['holdings'].items():
        if units > 0 and coin in current_prices:
            total += units * current_prices[coin]['price'] * BRL_USD_RATE
    return total

def calculate_runway(portfolio_value: float) -> float:
    """Calculate runway in months"""
    return portfolio_value / MONTHLY_BURN if MONTHLY_BURN > 0 else float('inf')

def simulate_trade(coin: str, action: str, price_usd: float, reason: str):
    """
    Execute simulated trade (paper trading)
    BUY: Allocate 10% of cash
    SELL: Liquidate 100% of holdings for that coin
    """
    if action == 'BUY':
        allocation_brl = portfolio['cash'] * 0.1  # 10% of cash
        if allocation_brl < 100:  # Minimum R$100
            log_message('warning', f'Insufficient cash for BUY {coin}', {'cash': portfolio['cash']})
            return

        amount_usd = allocation_brl / BRL_USD_RATE
        units = amount_usd / price_usd if price_usd > 0 else 0

        portfolio['cash'] -= allocation_brl
        portfolio['holdings'][coin] += units

        log_message('trade', f'BUY {coin.upper()}', {
            'amount_brl': allocation_brl,
            'units': units,
            'price_usd': price_usd,
            'reason': reason
        })

        # Save to DB
        cursor = db_conn.cursor()
        portfolio_value = calculate_portfolio_value({coin: {'price': price_usd}})
        runway = calculate_runway(portfolio_value)

        cursor.execute('''
            INSERT INTO trades (timestamp, coin, action, price_usd, amount_brl, units, grok_reason, portfolio_value_brl, runway_months)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            datetime.now().isoformat(),
            coin,
            'BUY',
            price_usd,
            allocation_brl,
            units,
            reason,
            portfolio_value,
            runway
        ))
        db_conn.commit()

    elif action == 'SELL':
        units = portfolio['holdings'][coin]
        if units <= 0:
            log_message('warning', f'No holdings to SELL {coin}')
            return

        amount_usd = units * price_usd
        amount_brl = amount_usd * BRL_USD_RATE

        portfolio['cash'] += amount_brl
        portfolio['holdings'][coin] = 0.0

        log_message('trade', f'SELL {coin.upper()}', {
            'amount_brl': amount_brl,
            'units': units,
            'price_usd': price_usd,
            'reason': reason
        })

        # Save to DB
        cursor = db_conn.cursor()
        portfolio_value = calculate_portfolio_value({coin: {'price': price_usd}})
        runway = calculate_runway(portfolio_value)

        cursor.execute('''
            INSERT INTO trades (timestamp, coin, action, price_usd, amount_brl, units, grok_reason, portfolio_value_brl, runway_months)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            datetime.now().isoformat(),
            coin,
            'SELL',
            price_usd,
            amount_brl,
            units,
            reason,
            portfolio_value,
            runway
        ))
        db_conn.commit()

def save_portfolio_snapshot(current_prices: Dict[str, Dict]):
    """Save current portfolio state to database"""
    total_value = calculate_portfolio_value(current_prices)
    runway = calculate_runway(total_value)

    cursor = db_conn.cursor()
    cursor.execute('''
        INSERT INTO portfolio_snapshots (timestamp, cash_brl, total_value_brl, runway_months, holdings_json)
        VALUES (?, ?, ?, ?, ?)
    ''', (
        datetime.now().isoformat(),
        portfolio['cash'],
        total_value,
        runway,
        json.dumps(portfolio['holdings'])
    ))
    db_conn.commit()

# ========== MAIN LOOP ==========

def main_loop():
    """Main trading loop"""
    log_message('info', '🤖 Grok-Powered Crypto Bot Started (Paper Mode)', {
        'coins': COINS,
        'interval': CHECK_INTERVAL,
        'threshold': GROK_ANALYZE_THRESHOLD,
        'initial_cash': INITIAL_CASH
    })

    iteration = 0

    while True:
        try:
            iteration += 1
            log_message('info', f'--- Iteration {iteration} ---')

            # Fetch prices
            current_prices = fetch_prices()
            if not current_prices:
                log_message('warning', 'No price data, skipping iteration')
                time.sleep(CHECK_INTERVAL)
                continue

            # Analyze each coin
            for coin, info in current_prices.items():
                price = info['price']
                change = info['change_1h']

                print(f"\n{coin.upper()}: ${price:,.4f} USD ({change:+.2f}% 1h)")

                # Grok analysis if significant change
                if abs(change) > GROK_ANALYZE_THRESHOLD * 100:
                    action, reason = grok_analyze(coin, price, change)
                    print(f"   🤖 Grok: {action} - {reason}")

                    # Execute trade
                    if action in ['BUY', 'SELL']:
                        simulate_trade(coin, action, price, reason)
                else:
                    print(f"   ⏸️ Stable: HOLD (under {GROK_ANALYZE_THRESHOLD*100}% threshold)")

            # Portfolio summary
            total_value = calculate_portfolio_value(current_prices)
            runway = calculate_runway(total_value)

            print(f"\n📊 Portfolio Summary:")
            print(f"   Cash: R${portfolio['cash']:,.0f}")
            for coin, units in portfolio['holdings'].items():
                if units > 0 and coin in current_prices:
                    value_brl = units * current_prices[coin]['price'] * BRL_USD_RATE
                    print(f"   {coin.upper()}: {units:.4f} units = R${value_brl:,.0f}")
            print(f"   Total Value: R${total_value:,.0f}")
            print(f"   Runway: {runway:.1f} months @ R${MONTHLY_BURN:,}/mo\n")

            # Save snapshot
            save_portfolio_snapshot(current_prices)

            # Wait for next iteration
            time.sleep(CHECK_INTERVAL)

        except KeyboardInterrupt:
            log_message('info', '🛑 Bot stopped by user')
            print(f"\nFinal Portfolio: Cash R${portfolio['cash']:,.0f}, Holdings: {portfolio['holdings']}")
            break
        except Exception as e:
            log_message('error', f'Main loop error: {e}')
            time.sleep(CHECK_INTERVAL)

# ========== ENTRY POINT ==========

if __name__ == "__main__":
    print("=" * 60)
    print("🤖 GROK CRYPTO TRADING BOT v1.0")
    print("=" * 60)

    # Initialize
    init_database()

    # Run
    try:
        main_loop()
    except Exception as e:
        log_message('error', f'Fatal error: {e}')
        sys.exit(1)
    finally:
        if db_conn:
            db_conn.close()
            log_message('info', 'Database connection closed')
