#!/usr/bin/env python3
"""
Huobi K-line Monitor Script v4
Monitors RIVER/USDT and ETH/USDT for continuous rise patterns and reversal signals.
Uses curl for API calls to avoid Python network issues.
"""

import json
import os
import subprocess
import time
from datetime import datetime
from typing import Dict, List, Optional, Tuple

# Configuration
API_BASE_URL = "https://api.huobi.pro/market/history/kline"
SYMBOLS = ["riverusdt", "ethusdt"]
PERIODS = ["1min", "5min", "15min"]
NOTIFICATION_FILE = "/root/.openclaw/workspace/memory/huobi-notified-v4.json"
COOLDOWN_SECONDS = 300  # 5 minutes

# Rating thresholds
RATING_THRESHOLDS = [
    (13, "SS"),
    (11, "S"),
    (9, "A"),
    (7, "B"),
    (5, "C"),
    (3, "D"),
]


def get_rating(consecutive_rises: int) -> str:
    """Get rating based on consecutive rise count."""
    for threshold, rating in RATING_THRESHOLDS:
        if consecutive_rises >= threshold:
            return rating
    return "N/A"


def load_notified_signals() -> Dict:
    """Load previously notified signals from file."""
    if os.path.exists(NOTIFICATION_FILE):
        try:
            with open(NOTIFICATION_FILE, "r") as f:
                return json.load(f)
        except (json.JSONDecodeError, IOError):
            return {}
    return {}


def save_notified_signals(signals: Dict) -> None:
    """Save notified signals to file."""
    os.makedirs(os.path.dirname(NOTIFICATION_FILE), exist_ok=True)
    with open(NOTIFICATION_FILE, "w") as f:
        json.dump(signals, f, indent=2)


def is_in_cooldown(signal_key: str, signals: Dict) -> bool:
    """Check if a signal is still in cooldown period."""
    if signal_key not in signals:
        return False
    last_notified = signals[signal_key]
    elapsed = time.time() - last_notified
    return elapsed < COOLDOWN_SECONDS


def fetch_kline_data(symbol: str, period: str) -> Optional[List[Dict]]:
    """Fetch K-line data from Huobi API using curl."""
    url = f"{API_BASE_URL}?symbol={symbol}&period={period}&size=30"
    try:
        result = subprocess.run(
            ["curl", "-s", "--max-time", "10", url],
            capture_output=True,
            text=True,
            timeout=15
        )
        if result.returncode != 0:
            return None
        data = json.loads(result.stdout)
        if data.get("status") == "ok" and "data" in data:
            return data["data"]
        return None
    except Exception as e:
        print(f"Error fetching {symbol} {period}: {e}")
        return None


def analyze_klines(klines: List[Dict], symbol: str, period: str) -> Tuple[Optional[dict], Optional[dict]]:
    """
    Analyze K-line data for continuous rise patterns.
    
    Strategy 1: Filtered Continuous Rise
    - Allow 1-2 reverse K-lines to interrupt counting
    - Only terminate counting after 3 consecutive reverse K-lines
    
    Returns: (rise_analysis, reversal_warning)
    """
    if not klines or len(klines) < 5:
        return None, None
    
    # Sort by timestamp ascending (oldest first)
    sorted_klines = sorted(klines, key=lambda x: x.get("id", 0))
    
    # Check current consecutive rises from the end
    current_consecutive = 0
    current_reverse = 0
    
    for i in range(len(sorted_klines) - 1, 0, -1):
        if sorted_klines[i].get("close", 0) > sorted_klines[i - 1].get("close", 0):
            if current_reverse > 0:
                break
            current_consecutive += 1
        else:
            current_reverse += 1
            if current_reverse >= 3:
                break
    
    # Calculate price change
    if len(sorted_klines) >= 3:
        first_close = sorted_klines[0].get("close", 0)
        last_close = sorted_klines[-1].get("close", 0)
        price_change_pct = ((last_close - first_close) / first_close) * 100 if first_close > 0 else 0
    else:
        price_change_pct = 0
    
    analysis = None
    reversal_warning = None
    
    if current_consecutive >= 3:
        rating = get_rating(current_consecutive)
        analysis = {
            "consecutive_rises": current_consecutive,
            "rating": rating,
            "symbol": symbol,
            "period": period,
            "last_close": sorted_klines[-1].get("close", 0),
            "price_change_pct": price_change_pct,
            "last_time": sorted_klines[-1].get("id", 0)
        }
        
        # Check for reversal warning (A grade or above = 7+)
        if current_consecutive >= 7:
            # Check reversal condition 1: 3 consecutive decline K-lines at the end
            decline_count = 0
            decline_details = []
            for i in range(len(sorted_klines) - 1, max(len(sorted_klines) - 4, 0), -1):
                curr_close = sorted_klines[i].get("close", 0)
                prev_close = sorted_klines[i - 1].get("close", 0)
                if curr_close < prev_close:
                    decline_count += 1
                    pct = ((curr_close - prev_close) / prev_close) * 100
                    decline_details.append(f"⬇️{pct:.1f}%")
                else:
                    break
            
            # Check reversal condition 2: Volume spike
            last_k = sorted_klines[-1]
            last_volume = last_k.get("vol", 0)
            prev_5 = sorted_klines[-6:-1] if len(sorted_klines) >= 6 else sorted_klines[:-1]
            avg_volume = sum(k.get("vol", 0) for k in prev_5) / len(prev_5) if prev_5 else 0
            
            volume_ratio = 0
            if avg_volume > 0:
                volume_ratio = last_volume / avg_volume
            
            if decline_count >= 3:
                reversal_warning = {
                    "type": "reversal_decline",
                    "reason": "3 consecutive decline K-lines",
                    "decline_details": " ".join(decline_details),
                    "symbol": symbol,
                    "period": period,
                    "consecutive_rises": current_consecutive,
                    "rating": rating,
                    "last_close": sorted_klines[-1].get("close", 0)
                }
            elif last_volume > 0 and avg_volume > 0 and volume_ratio >= 3:
                reversal_warning = {
                    "type": "reversal_volume",
                    "reason": f"Volume spike: {last_volume:.2f} vs avg {avg_volume:.2f} ({volume_ratio:.1f}x)",
                    "symbol": symbol,
                    "period": period,
                    "consecutive_rises": current_consecutive,
                    "rating": rating,
                    "last_close": sorted_klines[-1].get("close", 0),
                    "last_volume": last_volume,
                    "avg_volume": avg_volume,
                    "volume_ratio": volume_ratio
                }
    
    return analysis, reversal_warning


def main():
    """Main function to run the K-line monitor."""
    print(f"=== Huobi K-line Monitor v4 ===")
    print(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}")
    print()
    
    # Load notified signals
    notified_signals = load_notified_signals()
    current_time = time.time()
    
    # Clean up old entries (older than 1 hour)
    cleaned_signals = {
        k: v for k, v in notified_signals.items() 
        if current_time - v < 3600
    }
    
    all_rise_signals = []
    all_reversal_warnings = []
    
    for symbol in SYMBOLS:
        for period in PERIODS:
            # Fetch K-line data
            klines = fetch_kline_data(symbol, period)
            
            if not klines:
                print(f"❌ {symbol} {period}: Failed to fetch data")
                continue
            
            # Analyze for continuous rise patterns
            analysis, reversal_warning = analyze_klines(klines, symbol, period)
            
            if analysis:
                signal_key = f"{symbol}_{period}_rise"
                if is_in_cooldown(signal_key, cleaned_signals):
                    print(f"⏳ {symbol} {period}: Rise signal in cooldown")
                else:
                    all_rise_signals.append(analysis)
                    cleaned_signals[signal_key] = current_time
                    print(f"✅ {symbol} {period}: Rise signal - {analysis['consecutive_rises']} consecutive, {analysis['rating']} grade")
            
            if reversal_warning:
                signal_key = f"{symbol}_{period}_reversal"
                if is_in_cooldown(signal_key, cleaned_signals):
                    print(f"⏳ {symbol} {period}: Reversal warning in cooldown")
                else:
                    all_reversal_warnings.append(reversal_warning)
                    cleaned_signals[signal_key] = current_time
                    print(f"⚠️ {symbol} {period}: Reversal warning - was {reversal_warning['consecutive_rises']} consecutive ({reversal_warning['rating']})")
            
            if not analysis and not reversal_warning:
                print(f"📊 {symbol} {period}: No signal")
    
    # Save updated signals
    save_notified_signals(cleaned_signals)
    
    print()
    print("=" * 50)
    
    # Output rise signals
    for sig in all_rise_signals:
        pct = sig["price_change_pct"]
        print(f"""
📈 {sig['symbol'].upper()}/USDT - {sig['period']}级别
评级：{sig['rating']}级 (连续{sig['consecutive_rises']}根上涨)
当前价格：{sig['last_close']:.4f} USDT
连续涨幅：{pct:+.2f}%
""")
    
    # Output reversal warnings
    for warn in all_reversal_warnings:
        if warn["type"] == "reversal_decline":
            print(f"""
⚠️ {warn['symbol'].upper()}/USDT - {warn['period']}级别 - 反转预警！
刚才评级：{warn['rating']}级 → 可能见顶！
当前价格：{warn['last_close']:.4f} USDT
下跌序列：{warn['decline_details']}
""")
        elif warn["type"] == "reversal_volume":
            print(f"""
⚠️ {warn['symbol'].upper()}/USDT - {warn['period']}级别 - 反转预警！
刚才评级：{warn['rating']}级 → 可能见顶！
当前价格：{warn['last_close']:.4f} USDT
成交量异动：{warn['last_volume']:.2f}万 vs 平均{warn['avg_volume']:.2f}万 ({warn['volume_ratio']:.1f}倍)
""")
    
    if not all_rise_signals and not all_reversal_warnings:
        print("本次运行无信号。")
    
    return all_rise_signals, all_reversal_warnings


if __name__ == "__main__":
    main()
