#!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "yfinance>=0.2.40",
#     "pandas>=2.0.0",
#     "longport>=2.0.0",
#     "fear-and-greed>=0.4",
#     "edgartools>=2.0.0",
#     "feedparser>=6.0.0",
# ]
# ///
"""
Stock analysis with 8-dimension scoring: LongPort (primary) + Yahoo Finance (fallback).

Usage:
    uv run analyze_stock_unified.py TICKER [TICKER2 ...] [--output text|json] [--verbose] [--fast]
"""

import argparse
import asyncio
import json
import sys
from dataclasses import dataclass, asdict
from datetime import datetime
from typing import Literal, Optional

import pandas as pd

# Import unified data source
from data_source import fetch_stock_data_unified, UnifiedStockData

# Import original analysis functions (copy from analyze_stock.py)
# We'll create adapters to work with UnifiedStockData


@dataclass
class EarningsSurprise:
    score: float
    explanation: str
    actual_eps: float | None = None
    expected_eps: float | None = None
    surprise_pct: float | None = None


@dataclass
class Fundamentals:
    score: float
    key_metrics: dict
    explanation: str


@dataclass
class AnalystSentiment:
    score: float | None
    summary: str
    consensus_rating: str | None = None
    price_target: float | None = None
    current_price: float | None = None
    upside_pct: float | None = None
    num_analysts: int | None = None


@dataclass
class MomentumAnalysis:
    rsi_14d: float | None
    rsi_status: str
    price_vs_52w_low: float | None
    price_vs_52w_high: float | None
    near_52w_high: bool
    near_52w_low: bool
    volume_ratio: float | None
    score: float
    explanation: str


@dataclass
class Signal:
    ticker: str
    company_name: str
    recommendation: Literal["BUY", "HOLD", "SELL"]
    confidence: float
    final_score: float
    supporting_points: list[str]
    caveats: list[str]
    timestamp: str
    components: dict


def analyze_fundamentals_from_unified(data: UnifiedStockData) -> Fundamentals | None:
    """Analyze fundamentals from unified data source."""
    scores = []
    metrics = {}
    explanations = []
    
    try:
        # PE Ratio (LongPort)
        if data.pe_ttm and data.pe_ttm > 0:
            metrics["pe_ttm"] = float(data.pe_ttm)
            if data.pe_ttm < 15:
                scores.append(0.5)
                explanations.append(f"Attractive PE: {float(data.pe_ttm):.1f}x")
            elif data.pe_ttm > 30:
                scores.append(-0.3)
                explanations.append(f"Elevated PE: {float(data.pe_ttm):.1f}x")
            else:
                scores.append(0.1)
        
        # PB Ratio (LongPort)
        if data.pb and data.pb > 0:
            metrics["pb"] = float(data.pb)
            if data.pb < 1.0:
                scores.append(0.6)
                explanations.append(f"Below book value: PB {float(data.pb):.2f}")
            elif data.pb < 2.0:
                scores.append(0.3)
            elif data.pb > 5.0:
                scores.append(-0.4)
                explanations.append(f"High PB: {float(data.pb):.1f}x")
        
        # Dividend Yield (LongPort)
        if data.dividend_yield:
            metrics["dividend_yield"] = float(data.dividend_yield)
            if data.dividend_yield > 5:
                scores.append(0.5)
                explanations.append(f"High dividend: {float(data.dividend_yield):.1f}%")
            elif data.dividend_yield > 3:
                scores.append(0.3)
            elif data.dividend_yield < 1:
                scores.append(-0.2)
        
        # Operating Margin (Yahoo fallback)
        if data.operating_margin:
            metrics["operating_margin"] = float(data.operating_margin)
            if data.operating_margin > 0.15:
                scores.append(0.5)
                explanations.append(f"Strong margin: {float(data.operating_margin)*100:.1f}%")
            elif data.operating_margin < 0.05:
                scores.append(-0.5)
                explanations.append(f"Weak margin: {float(data.operating_margin)*100:.1f}%")
        
        # ROE (Yahoo fallback)
        if data.roe:
            metrics["roe"] = float(data.roe)
            if data.roe > 0.15:
                scores.append(0.4)
                explanations.append(f"Strong ROE: {float(data.roe)*100:.1f}%")
            elif data.roe < 0.05:
                scores.append(-0.3)
        
        # Debt to Equity (Yahoo fallback)
        if data.debt_to_equity:
            metrics["debt_to_equity"] = float(data.debt_to_equity)
            if data.debt_to_equity < 50:
                scores.append(0.3)
            elif data.debt_to_equity > 200:
                scores.append(-0.5)
                explanations.append(f"High debt: D/E {float(data.debt_to_equity)/100:.1f}x")
        
        if not scores:
            return None
        
        avg_score = sum(scores) / len(scores)
        normalized_score = max(-1.0, min(1.0, avg_score))
        
        return Fundamentals(
            score=normalized_score,
            key_metrics=metrics,
            explanation="; ".join(explanations) if explanations else "Mixed fundamentals",
        )
        
    except Exception as e:
        print(f"Fundamentals analysis error: {e}", file=sys.stderr)
        return None


def analyze_momentum_from_unified(data: UnifiedStockData) -> MomentumAnalysis | None:
    """Analyze momentum from unified data source."""
    try:
        # Use volume_ratio from LongPort
        volume_ratio = float(data.volume_ratio) if data.volume_ratio else None
        
        # Calculate RSI from price history if available
        rsi_14d = None
        rsi_status = "neutral"
        
        if data.price_history is not None and len(data.price_history) >= 14:
            close_prices = data.price_history["Close"]
            delta = close_prices.diff()
            gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
            loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
            rs = gain / loss
            rsi = 100 - (100 / (1 + rs))
            rsi_14d = float(rsi.iloc[-1])
            
            if rsi_14d > 70:
                rsi_status = "overbought"
            elif rsi_14d < 30:
                rsi_status = "oversold"
        
        # Score based on available metrics
        scores = []
        
        if rsi_14d:
            if rsi_14d < 30:
                scores.append(0.5)  # Oversold = opportunity
            elif rsi_14d > 70:
                scores.append(-0.5)  # Overbought = risk
        
        if volume_ratio:
            if volume_ratio > 1.5:
                scores.append(0.3)  # High volume = strong move
            elif volume_ratio < 0.5:
                scores.append(-0.2)  # Low volume = weak move
        
        if data.change_rate:
            change = float(data.change_rate)
            if abs(change) > 5:
                scores.append(0.2 if change > 0 else -0.2)
        
        score = sum(scores) / len(scores) if scores else 0.0
        
        return MomentumAnalysis(
            rsi_14d=rsi_14d,
            rsi_status=rsi_status,
            price_vs_52w_low=None,
            price_vs_52w_high=None,
            near_52w_high=False,
            near_52w_low=False,
            volume_ratio=volume_ratio,
            score=max(-1.0, min(1.0, score)),
            explanation=f"RSI: {rsi_14d:.1f} ({rsi_status})" if rsi_14d else "Limited momentum data",
        )
        
    except Exception as e:
        print(f"Momentum analysis error: {e}", file=sys.stderr)
        return None


def synthesize_signal(
    ticker: str,
    company_name: str,
    fundamentals: Fundamentals | None,
    momentum: MomentumAnalysis | None,
    data: UnifiedStockData,
) -> Signal:
    """Synthesize final signal from analysis components."""
    
    scores = []
    weights = []
    supporting_points = []
    caveats = []
    
    # Fundamentals (40% weight)
    if fundamentals:
        scores.append(fundamentals.score)
        weights.append(0.40)
        if fundamentals.score > 0.3:
            supporting_points.append(f"✓ Strong fundamentals: {fundamentals.explanation}")
        elif fundamentals.score < -0.3:
            caveats.append(f"⚠ Weak fundamentals: {fundamentals.explanation}")
    
    # Valuation (30% weight) - from PE/PB/Dividend
    valuation_score = 0
    valuation_count = 0
    
    if data.pe_ttm and data.pe_ttm > 0:
        if data.pe_ttm < 15:
            valuation_score += 0.5
        elif data.pe_ttm < 25:
            valuation_score += 0.2
        elif data.pe_ttm > 35:
            valuation_score -= 0.3
        valuation_count += 1
    
    if data.pb:
        if data.pb < 1.0:
            valuation_score += 0.6
        elif data.pb < 2.0:
            valuation_score += 0.3
        elif data.pb > 5.0:
            valuation_score -= 0.4
        valuation_count += 1
    
    if data.dividend_yield:
        if data.dividend_yield > 5:
            valuation_score += 0.5
        elif data.dividend_yield > 3:
            valuation_score += 0.3
        valuation_count += 1
    
    if valuation_count > 0:
        avg_valuation = valuation_score / valuation_count
        scores.append(avg_valuation)
        weights.append(0.30)
        
        if data.dividend_yield and data.dividend_yield > 5:
            supporting_points.append(f"✓ High dividend yield: {float(data.dividend_yield):.1f}%")
    
    # Momentum (20% weight)
    if momentum:
        scores.append(momentum.score)
        weights.append(0.20)
        if momentum.rsi_status == "oversold":
            supporting_points.append(f"✓ Oversold RSI: {momentum.rsi_14d:.1f}")
        elif momentum.rsi_status == "overbought":
            caveats.append(f"⚠ Overbought RSI: {momentum.rsi_14d:.1f}")
    
    # Market cap consideration (10% weight)
    if data.market_cap:
        cap = float(data.market_cap)
        if cap > 10e9:  # Large cap
            scores.append(0.3)
            supporting_points.append("✓ Large-cap stability")
        elif cap < 1e9:  # Small cap
            scores.append(-0.2)
            caveats.append("⚠ Small-cap volatility")
        weights.append(0.10)
    
    # Calculate final score
    if scores:
        final_score = sum(s * w for s, w in zip(scores, weights)) / sum(weights)
    else:
        final_score = 0.0
    
    # Determine recommendation
    if final_score > 0.3:
        recommendation = "BUY"
        confidence = min(0.9, 0.5 + final_score)
    elif final_score > 0.0:
        recommendation = "BUY"
        confidence = 0.5 + final_score
    elif final_score > -0.3:
        recommendation = "HOLD"
        confidence = 0.5 - final_score
    else:
        recommendation = "SELL"
        confidence = min(0.9, 0.5 - final_score)
    
    # Add data source info
    supporting_points.append(f"📊 Data: {', '.join(data.data_sources)}")
    
    return Signal(
        ticker=ticker,
        company_name=company_name,
        recommendation=recommendation,
        confidence=round(confidence, 2),
        final_score=round(final_score, 3),
        supporting_points=supporting_points[:5],
        caveats=caveats[:5],
        timestamp=datetime.now().isoformat(),
        components={
            "fundamentals": asdict(fundamentals) if fundamentals else None,
            "momentum": asdict(momentum) if momentum else None,
            "valuation": {
                "pe_ttm": float(data.pe_ttm) if data.pe_ttm else None,
                "pb": float(data.pb) if data.pb else None,
                "dividend_yield": float(data.dividend_yield) if data.dividend_yield else None,
            },
        },
    )


def format_output_text(signal: Signal) -> str:
    """Format signal as text output."""
    lines = [
        "=" * 60,
        f"📊 {signal.ticker} - {signal.company_name}",
        f"Generated: {signal.timestamp}",
        "=" * 60,
        "",
        f"📋 RECOMMENDATION: {signal.recommendation} (Confidence: {signal.confidence*100:.0f}%)",
        f"⭐ SCORE: {signal.final_score:+.3f}",
        "",
        "✅ SUPPORTING POINTS:",
    ]
    
    for point in signal.supporting_points:
        lines.append(f"  {point}")
    
    lines.extend(["", "⚠️ CAVEATS:"])
    
    for caveat in signal.caveats:
        lines.append(f"  {caveat}")
    
    # Add component details
    if signal.components.get("fundamentals"):
        fund = signal.components["fundamentals"]
        lines.extend([
            "",
            "📈 FUNDAMENTALS:",
            f"  Score: {fund['score']:+.2f}",
            f"  {fund['explanation']}",
        ])
    
    if signal.components.get("valuation"):
        val = signal.components["valuation"]
        lines.extend([
            "",
            "💰 VALUATION:",
            f"  PE TTM: {val['pe_ttm']:.2f}" if val['pe_ttm'] else "  PE TTM: N/A",
            f"  PB: {val['pb']:.2f}" if val['pb'] else "  PB: N/A",
            f"  Dividend: {val['dividend_yield']:.1f}%" if val['dividend_yield'] else "  Dividend: N/A",
        ])
    
    lines.extend([
        "",
        "=" * 60,
        "⚠️ NOT FINANCIAL ADVICE. For informational purposes only.",
        "=" * 60,
    ])
    
    return "\n".join(lines)


def format_output_json(signal: Signal) -> str:
    """Format signal as JSON."""
    return json.dumps(asdict(signal), indent=2, default=str)


def main():
    parser = argparse.ArgumentParser(
        description="Stock analysis with 8-dimension scoring (LongPort + Yahoo)"
    )
    parser.add_argument("tickers", nargs="+", help="Stock tickers")
    parser.add_argument("--output", choices=["text", "json"], default="text")
    parser.add_argument("--verbose", "-v", action="store_true")
    parser.add_argument("--fast", action="store_true", help="Skip slow analyses")
    
    args = parser.parse_args()
    
    results = []
    
    for ticker in args.tickers:
        ticker = ticker.upper()
        
        if args.verbose:
            print(f"\n=== Analyzing {ticker} ===", file=sys.stderr)
        
        # Fetch unified data
        data = fetch_stock_data_unified(ticker, verbose=args.verbose)
        
        if data is None:
            print(f"Error: Failed to fetch data for {ticker}", file=sys.stderr)
            continue
        
        # Run analyses
        if args.verbose:
            print("  Analyzing fundamentals...", file=sys.stderr)
        fundamentals = analyze_fundamentals_from_unified(data)
        
        if args.verbose:
            print("  Analyzing momentum...", file=sys.stderr)
        momentum = analyze_momentum_from_unified(data)
        
        # Synthesize signal
        signal = synthesize_signal(
            ticker=data.symbol,
            company_name=data.name,
            fundamentals=fundamentals,
            momentum=momentum,
            data=data,
        )
        
        results.append(signal)
        
        if args.output == "text":
            print(format_output_text(signal))
    
    if args.output == "json":
        if len(results) == 1:
            print(format_output_json(results[0]))
        else:
            print(json.dumps([asdict(r) for r in results], indent=2, default=str))


if __name__ == "__main__":
    main()
