#!/usr/bin/env python3
"""
Crypto Short Signal Scanner
Scans for overbought coins ripe for shorting.
Criteria: high RSI, price above VWAP, fading momentum, bearish divergence.

Zero AI tokens — runs as pure Python via systemd timer.
"""

import json
import os
import sys
import time
import math
import urllib.request
from datetime import datetime, timezone, timedelta
from pathlib import Path

# Config
DATA_DIR = Path(__file__).parent.parent / "data" / "short-scanner"
DATA_DIR.mkdir(parents=True, exist_ok=True)
SCAN_LOG = DATA_DIR / "scan_log.json"

TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "6443752046")

BINANCE_KLINES = "https://api.binance.us/api/v3/klines"
BINANCE_TICKER = "https://api.binance.us/api/v3/ticker/24hr"

# Coins to scan (popular leveraged trading coins)
COINS = [
    "BTCUSDT", "ETHUSDT", "SOLUSDT", "XRPUSDT", "DOGEUSDT",
    "ADAUSDT", "AVAXUSDT", "LINKUSDT", "DOTUSDT", "MATICUSDT",
    "NEARUSDT", "ATOMUSDT", "LTCUSDT", "UNIUSDT", "AAVEUSDT",
    "FILUSDT", "ALGOUSDT", "XLMUSDT", "VETUSDT", "ICPUSDT",
    "APTUSDT", "SUIUSDT", "ARBUSDT", "OPUSDT", "SEIUSDT",
    "HYPEUSDT", "TRUMPUSDT", "PUMPUSDT", "ASTERUSDT",
]


def get_klines(symbol, interval='1h', limit=100):
    """Fetch klines from Binance US."""
    url = f"{BINANCE_KLINES}?symbol={symbol}&interval={interval}&limit={limit}"
    req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
    try:
        resp = urllib.request.urlopen(req, timeout=10)
        raw = json.loads(resp.read())
        return [{
            'open': float(k[1]),
            'high': float(k[2]),
            'low': float(k[3]),
            'close': float(k[4]),
            'volume': float(k[5]),
            'close_time': k[6],
        } for k in raw]
    except:
        return []


def calc_rsi(closes, period=14):
    """Calculate RSI."""
    if len(closes) < period + 1:
        return 50
    deltas = [closes[i] - closes[i-1] for i in range(1, len(closes))]
    gains = [d if d > 0 else 0 for d in deltas]
    losses = [-d if d < 0 else 0 for d in deltas]
    
    avg_gain = sum(gains[:period]) / period
    avg_loss = sum(losses[:period]) / period
    
    for i in range(period, len(deltas)):
        avg_gain = (avg_gain * (period - 1) + gains[i]) / period
        avg_loss = (avg_loss * (period - 1) + losses[i]) / period
    
    if avg_loss == 0:
        return 100
    rs = avg_gain / avg_loss
    return round(100 - (100 / (1 + rs)), 1)


def calc_vwap(klines):
    """Calculate VWAP from klines."""
    cum_vol = 0
    cum_tp_vol = 0
    for k in klines:
        tp = (k['high'] + k['low'] + k['close']) / 3
        cum_vol += k['volume']
        cum_tp_vol += tp * k['volume']
    if cum_vol == 0:
        return 0
    return cum_tp_vol / cum_vol


def calc_ema(values, period):
    """Calculate EMA."""
    if not values:
        return 0
    multiplier = 2 / (period + 1)
    ema = values[0]
    for v in values[1:]:
        ema = (v - ema) * multiplier + ema
    return ema


def calc_macd(closes):
    """Calculate MACD (12, 26, 9)."""
    if len(closes) < 26:
        return 0, 0, 0
    ema12 = calc_ema(closes, 12)
    ema26 = calc_ema(closes, 26)
    macd_line = ema12 - ema26
    # Approximate signal line
    signal = calc_ema(closes[-9:], 9) if len(closes) >= 9 else macd_line
    histogram = macd_line - signal
    return macd_line, signal, histogram


def calc_bollinger_position(closes, period=20):
    """How far price is from upper Bollinger band. >1 = above upper band."""
    if len(closes) < period:
        return 0.5
    recent = closes[-period:]
    sma = sum(recent) / period
    std = (sum((x - sma)**2 for x in recent) / period) ** 0.5
    if std == 0:
        return 0.5
    upper = sma + 2 * std
    lower = sma - 2 * std
    band_width = upper - lower
    if band_width == 0:
        return 0.5
    return (closes[-1] - lower) / band_width


def volume_trend(klines, lookback=10):
    """Compare recent volume to average. >1 means increasing volume."""
    if len(klines) < lookback * 2:
        return 1.0
    recent_vol = sum(k['volume'] for k in klines[-lookback:]) / lookback
    older_vol = sum(k['volume'] for k in klines[-lookback*2:-lookback]) / lookback
    if older_vol == 0:
        return 1.0
    return recent_vol / older_vol


def scan_coin(symbol):
    """Analyze a single coin for short signals."""
    # Get 1h candles for RSI/VWAP/indicators
    klines_1h = get_klines(symbol, '1h', 100)
    if len(klines_1h) < 30:
        return None
    
    closes = [k['close'] for k in klines_1h]
    current_price = closes[-1]
    
    # RSI (14-period on 1h)
    rsi = calc_rsi(closes)
    
    # VWAP (24h)
    vwap_24h = calc_vwap(klines_1h[-24:])
    vwap_pct = ((current_price - vwap_24h) / vwap_24h * 100) if vwap_24h else 0
    
    # MACD
    macd_line, signal_line, histogram = calc_macd(closes)
    macd_bearish = histogram < 0  # Below signal = bearish
    
    # Bollinger position
    bb_pos = calc_bollinger_position(closes)
    
    # Volume trend
    vol_trend = volume_trend(klines_1h)
    
    # 24h change
    price_24h_ago = closes[-24] if len(closes) >= 24 else closes[0]
    change_24h = ((current_price - price_24h_ago) / price_24h_ago * 100) if price_24h_ago else 0
    
    # 4h change (momentum)
    price_4h_ago = closes[-4] if len(closes) >= 4 else closes[0]
    change_4h = ((current_price - price_4h_ago) / price_4h_ago * 100) if price_4h_ago else 0
    
    # === SHORT SCORING ===
    score = 0
    reasons = []
    
    # RSI overbought (max 30 pts)
    if rsi >= 80:
        score += 30
        reasons.append(f"RSI extremely overbought ({rsi})")
    elif rsi >= 70:
        score += 25
        reasons.append(f"RSI overbought ({rsi})")
    elif rsi >= 65:
        score += 15
        reasons.append(f"RSI elevated ({rsi})")
    elif rsi >= 60:
        score += 5
        reasons.append(f"RSI mildly elevated ({rsi})")
    
    # Price above VWAP (max 20 pts)
    if vwap_pct > 5:
        score += 20
        reasons.append(f"Well above VWAP (+{vwap_pct:.1f}%)")
    elif vwap_pct > 3:
        score += 15
        reasons.append(f"Above VWAP (+{vwap_pct:.1f}%)")
    elif vwap_pct > 1:
        score += 8
        reasons.append(f"Slightly above VWAP (+{vwap_pct:.1f}%)")
    
    # MACD bearish crossover (max 15 pts)
    if macd_bearish and histogram < -0.001 * current_price:
        score += 15
        reasons.append("MACD bearish + accelerating")
    elif macd_bearish:
        score += 10
        reasons.append("MACD bearish crossover")
    
    # Bollinger band position (max 15 pts)
    if bb_pos > 1.0:
        score += 15
        reasons.append(f"Above upper Bollinger ({bb_pos:.2f})")
    elif bb_pos > 0.85:
        score += 10
        reasons.append(f"Near upper Bollinger ({bb_pos:.2f})")
    
    # Big recent pump (mean reversion candidate) (max 15 pts)
    if change_24h > 15:
        score += 15
        reasons.append(f"Pumped +{change_24h:.1f}% 24h")
    elif change_24h > 8:
        score += 10
        reasons.append(f"Up +{change_24h:.1f}% 24h")
    elif change_24h > 4:
        score += 5
        reasons.append(f"Up +{change_24h:.1f}% 24h")
    
    # Volume fading on uptrend (exhaustion) (5 pts)
    if change_24h > 2 and vol_trend < 0.7:
        score += 5
        reasons.append("Volume fading on uptrend (exhaustion)")
    
    return {
        "symbol": symbol.replace("USDT", ""),
        "price": current_price,
        "rsi": rsi,
        "vwap_pct": round(vwap_pct, 2),
        "macd_histogram": round(histogram, 6),
        "bb_position": round(bb_pos, 2),
        "change_24h": round(change_24h, 2),
        "change_4h": round(change_4h, 2),
        "vol_trend": round(vol_trend, 2),
        "score": score,
        "reasons": reasons,
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }


def send_telegram_alert(message):
    """Send alert via Telegram bot API."""
    if not TELEGRAM_BOT_TOKEN:
        print(f"[ALERT] {message}")
        return
    url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
    data = json.dumps({
        "chat_id": TELEGRAM_CHAT_ID,
        "text": message,
        "parse_mode": "HTML"
    }).encode()
    req = urllib.request.Request(url, data=data, headers={
        "Content-Type": "application/json",
        "User-Agent": "Mozilla/5.0"
    })
    try:
        urllib.request.urlopen(req, timeout=10)
    except Exception as e:
        print(f"Telegram alert failed: {e}")


def main():
    print(f"=== Crypto Short Signal Scanner ===")
    print(f"Time: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}")
    print()
    
    results = []
    for symbol in COINS:
        result = scan_coin(symbol)
        if result:
            results.append(result)
        time.sleep(0.15)  # Rate limiting
    
    # Sort by score descending
    results.sort(key=lambda x: x['score'], reverse=True)
    
    # Print all results
    for r in results:
        emoji = "🔴" if r['score'] >= 50 else "🟡" if r['score'] >= 30 else "⚪"
        print(f"{emoji} {r['symbol']:8s} score:{r['score']:3d} | RSI:{r['rsi']:5.1f} | VWAP:{r['vwap_pct']:+6.1f}% | 24h:{r['change_24h']:+6.1f}% | BB:{r['bb_position']:.2f}")
        if r['reasons']:
            for reason in r['reasons']:
                print(f"   → {reason}")
    
    # Alert on strong short signals (score >= 50)
    strong = [r for r in results if r['score'] >= 50]
    if strong:
        lines = ["🔴 <b>Short Signals Detected</b>\n"]
        for r in strong:
            lines.append(f"<b>{r['symbol']}</b> (score: {r['score']})")
            lines.append(f"  Price: ${r['price']:.4f} | RSI: {r['rsi']} | VWAP: {r['vwap_pct']:+.1f}%")
            lines.append(f"  24h: {r['change_24h']:+.1f}% | BB: {r['bb_position']:.2f}")
            for reason in r['reasons']:
                lines.append(f"  → {reason}")
            lines.append("")
        send_telegram_alert("\n".join(lines))
    
    # Save scan log
    log = []
    if SCAN_LOG.exists():
        try:
            log = json.loads(SCAN_LOG.read_text())
        except:
            pass
    
    log.append({
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "coins_scanned": len(results),
        "strong_signals": len(strong),
        "results": results,
    })
    log = log[-500:]
    SCAN_LOG.write_text(json.dumps(log, indent=2))
    
    print(f"\n📊 Summary: {len(results)} scanned, {len(strong)} strong short signals")


if __name__ == "__main__":
    main()
