#!/usr/bin/env python3
"""
Generate jump-in/jump-out signals from Binance order flow graph.

This script:
1. Loads order flow graph with anomalies
2. Generates trading signals (entry, stop, target)
3. Calculates confidence and risk-reward
4. Outputs actionable signals
"""

import json
from pathlib import Path
from typing import Dict, List
from datetime import datetime


class SignalGenerator:
    """Generate trading signals from order flow anomalies."""

    def __init__(self, graph_file: Path):
        self.graph_file = graph_file
        self.graph = self.load_graph()

    def load_graph(self) -> Dict:
        """Load order flow graph from JSON."""
        print(f"Loading graph from {self.graph_file}...")

        with open(self.graph_file, 'r') as f:
            graph = json.load(f)

        print(f"  ✓ Loaded {len(graph['entities'])} entities, {len(graph['relationships'])} relationships")
        return graph

    def get_anomalies(self) -> List[Dict]:
        """Extract all anomalies from graph."""
        anomalies = []

        for entity in self.graph['entities']:
            if entity['type'] == 'Anomaly':
                anomaly = {
                    'id': entity['id'],
                    'name': entity['name'],
                    'type': entity['properties']['type'],
                    'direction': entity['properties']['direction'],
                    'severity': entity['properties']['severity'],
                    'reason': entity['properties'].get('reason', '')
                }

                # Find associated price level
                for rel in self.graph['relationships']:
                    if rel['type'] == 'HAS_ANOMALY' and rel['to'] == entity['id']:
                        price_level_id = rel['from']
                        price_entity = next((e for e in self.graph['entities'] if e['id'] == price_level_id), None)
                        if price_entity:
                            anomaly['price'] = price_entity['properties']['price']
                            anomaly['metrics'] = price_entity['properties']
                        break

                if 'price' in anomaly:
                    anomalies.append(anomaly)

        return anomalies

    def generate_absorption_signal(self, anomaly: Dict) -> Dict:
        """Generate signal from absorption anomaly."""
        direction = anomaly['direction']
        entry_price = anomaly['price']
        severity = anomaly['severity']
        wall_size = anomaly['metrics']['wall_strength']

        # Absorption = smart money defending level → trade in direction of wall
        # Calculate targets and stops based on wall size
        price_impact = min(wall_size / 5.0, 5.0)  # Cap at 5 points

        if direction == 'bullish':
            # Bullish absorption = bid wall absorbing sellers → go long
            target = entry_price + price_impact
            stop = entry_price - (price_impact * 0.25)  # Tight stop (25% of target)
        else:
            # Bearish absorption = ask wall absorbing buyers → go short
            target = entry_price - price_impact
            stop = entry_price + (price_impact * 0.25)

        return {
            'type': 'ABSORPTION',
            'direction': direction,
            'entry_price': round(entry_price, 2),
            'target_price': round(target, 2),
            'stop_price': round(stop, 2),
            'confidence': round(min(severity + 0.15, 0.95), 2),
            'risk_reward': round(abs(target - entry_price) / abs(entry_price - stop), 2),
            'wall_size': round(wall_size, 2),
            'reason': anomaly['reason'],
            'timestamp': datetime.now().isoformat()
        }

    def generate_squeeze_signal(self, anomaly: Dict) -> Dict:
        """Generate signal from squeeze anomaly."""
        direction = anomaly['direction']
        entry_price = anomaly['price']
        severity = anomaly['severity']

        # Squeeze = coiled spring → wait for breakout, then jump in
        # For now, assume entry at current level with very tight stop
        price_impact = 3.0  # Squeezes can explode 3+ points

        if direction == 'bullish':
            target = entry_price + price_impact
            stop = entry_price - 0.5  # Very tight stop
        else:
            target = entry_price - price_impact
            stop = entry_price + 0.5

        return {
            'type': 'SQUEEZE',
            'direction': direction,
            'entry_price': round(entry_price, 2),
            'target_price': round(target, 2),
            'stop_price': round(stop, 2),
            'confidence': round(min(severity + 0.10, 0.85), 2),
            'risk_reward': round(abs(target - entry_price) / abs(entry_price - stop), 2),
            'reason': anomaly['reason'],
            'note': 'Wait for breakout confirmation before entering',
            'timestamp': datetime.now().isoformat()
        }

    def generate_exhaustion_signal(self, anomaly: Dict) -> Dict:
        """Generate signal from exhaustion anomaly."""
        direction = anomaly['direction']
        entry_price = anomaly['price']
        severity = anomaly['severity']

        # Exhaustion = wall depleted → reversal play
        price_impact = 2.0  # Smaller moves expected

        if direction == 'bullish':
            target = entry_price + price_impact
            stop = entry_price - 0.5
        else:  # bearish
            target = entry_price - price_impact
            stop = entry_price + 0.5

        return {
            'type': 'EXHAUSTION',
            'direction': direction,
            'entry_price': round(entry_price, 2),
            'target_price': round(target, 2),
            'stop_price': round(stop, 2),
            'confidence': round(min(severity + 0.05, 0.75), 2),  # Lower confidence
            'risk_reward': round(abs(target - entry_price) / abs(entry_price - stop), 2),
            'reason': anomaly['reason'],
            'note': 'Reversal play — wait for confirmation',
            'timestamp': datetime.now().isoformat()
        }

    def generate_all_signals(self) -> List[Dict]:
        """Generate trading signals from all anomalies."""
        print("\n" + "="*80)
        print("GENERATING TRADING SIGNALS")
        print("="*80)

        anomalies = self.get_anomalies()
        signals = []

        print(f"\nFound {len(anomalies)} anomalies")

        # Group by price to avoid duplicate signals
        processed_prices = set()

        for anomaly in anomalies:
            price = anomaly['price']

            # Skip if we already processed this price (use best signal)
            if price in processed_prices:
                continue

            processed_prices.add(price)

            print(f"\nProcessing: {anomaly['type']} at ${price:.2f}")

            if anomaly['type'] == 'ABSORPTION':
                signal = self.generate_absorption_signal(anomaly)
                signals.append(signal)
                print(f"  ✓ Signal: {signal['direction'].upper()} {signal['type']}")
                print(f"    Entry: ${signal['entry_price']}, Target: ${signal['target_price']}, Stop: ${signal['stop_price']}")
                print(f"    Confidence: {signal['confidence']}, R:R: {signal['risk_reward']}")

            elif anomaly['type'] == 'SQUEEZE':
                # Only generate signals for high-confidence squeezes
                if anomaly['severity'] > 0.8:
                    signal = self.generate_squeeze_signal(anomaly)
                    signals.append(signal)
                    print(f"  ✓ Signal: {signal['direction'].upper()} {signal['type']}")
                    print(f"    Entry: ${signal['entry_price']}, Target: ${signal['target_price']}, Stop: ${signal['stop_price']}")
                    print(f"    Confidence: {signal['confidence']}, R:R: {signal['risk_reward']}")
                else:
                    print(f"  ⊘ Skipped: Low severity squeeze")

            elif anomaly['type'] == 'EXHAUSTION':
                # Only generate signals for high-severity exhaustion
                if anomaly['severity'] > 0.7:
                    signal = self.generate_exhaustion_signal(anomaly)
                    signals.append(signal)
                    print(f"  ✓ Signal: {signal['direction'].upper()} {signal['type']}")
                    print(f"    Entry: ${signal['entry_price']}, Target: ${signal['target_price']}, Stop: ${signal['stop_price']}")
                    print(f"    Confidence: {signal['confidence']}, R:R: {signal['risk_reward']}")
                else:
                    print(f"  ⊘ Skipped: Low severity exhaustion")

        # Sort by confidence * risk_reward
        signals.sort(key=lambda x: x['confidence'] * x['risk_reward'], reverse=True)

        print(f"\n✓ Generated {len(signals)} trading signals")

        return signals

    def save_signals(self, signals: List[Dict], filename: str):
        """Save signals to JSON file."""
        output_file = self.graph_file.parent / filename

        output_data = {
            'metadata': {
                'generated_at': datetime.now().isoformat(),
                'signal_count': len(signals),
                'source_graph': str(self.graph_file)
            },
            'signals': signals
        }

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

        print(f"\n✓ Saved signals to {output_file}")


def print_signal_summary(signals: List[Dict]):
    """Print summary of generated signals."""
    print("\n" + "="*80)
    print("SIGNAL SUMMARY")
    print("="*80)

    # Group by type
    by_type = {
        'ABSORPTION': [],
        'SQUEEZE': [],
        'EXHAUSTION': []
    }

    for signal in signals:
        by_type[signal['type']].append(signal)

    for stype, sigs in by_type.items():
        if sigs:
            print(f"\n{stype} ({len(sigs)} signals):")

            for i, sig in enumerate(sigs[:5], 1):  # Show top 5
                print(f"  {i}. {sig['direction'].upper()}")
                print(f"     Entry: ${sig['entry_price']}, Target: ${sig['target_price']}, Stop: ${sig['stop_price']}")
                print(f"     Confidence: {sig['confidence']}, R:R: {sig['risk_reward']}")

    # Show best opportunities
    print("\n" + "="*80)
    print("🎯 BEST TRADING OPPORTUNITIES (Confidence > 0.7, R:R > 2.0)")
    print("="*80)

    best_signals = [s for s in signals if s['confidence'] > 0.7 and s['risk_reward'] > 2.0]
    best_signals.sort(key=lambda x: x['confidence'] * x['risk_reward'], reverse=True)

    if best_signals:
        for i, sig in enumerate(best_signals[:10], 1):
            print(f"\n{i}. {sig['type']} - {sig['direction'].upper()}")
            print(f"   {sig['reason']}")
            print(f"   Entry: ${sig['entry_price']}, Target: ${sig['target_price']}, Stop: ${sig['stop_price']}")
            print(f"   Confidence: {sig['confidence']}, Risk-Reward: {sig['risk_reward']}")
            if 'note' in sig:
                print(f"   Note: {sig['note']}")
    else:
        print("\nNo signals met criteria (Confidence > 0.7, R:R > 2.0)")

    # Show all ABSORPTION signals (highest quality)
    print("\n" + "="*80)
    print("💎 ABSORPTION SIGNALS (Highest Quality)")
    print("="*80)

    absorption_signals = [s for s in signals if s['type'] == 'ABSORPTION']

    if absorption_signals:
        for i, sig in enumerate(absorption_signals[:5], 1):
            print(f"\n{i}. {sig['direction'].upper()} ABSORPTION")
            print(f"   {sig['reason']}")
            print(f"   Entry: ${sig['entry_price']}, Target: ${sig['target_price']}, Stop: ${sig['stop_price']}")
            print(f"   Confidence: {sig['confidence']}, Risk-Reward: {sig['risk_reward']}")
            print(f"   Wall Size: {sig['wall_size']} contracts")
    else:
        print("\nNo absorption signals found")


def main():
    """Main signal generation script."""
    print("="*80)
    print("ORDER FLOW SIGNAL GENERATOR (BINANCE DATA)")
    print("="*80)

    # Paths
    graph_file = Path("/home/ubuntu/.hermes/workspace/projects/ORDER_FLOW_GRAPH/data/order_flow_graph_binance.json")

    # Generate signals
    generator = SignalGenerator(graph_file)
    signals = generator.generate_all_signals()

    # Save signals
    generator.save_signals(signals, "signals_binance.json")

    # Print summary
    print_signal_summary(signals)

    print("\n" + "="*80)
    print("SIGNAL GENERATION COMPLETE")
    print("="*80)


if __name__ == "__main__":
    main()
