#!/usr/bin/env python3
"""Deep dive analysis on BAC, CFG, FITB, INCY + expanded GARP scan."""

import yfinance as yf
import json
import datetime
import numpy as np
from pathlib import Path

TARGETS = ["BAC", "CFG", "FITB", "INCY"]

def safe_get(d, *keys, default=None):
    for k in keys:
        if isinstance(d, dict):
            d = d.get(k, default)
        else:
            return default
    return d

def analyze_stock(ticker_str):
    print(f"\n{'='*60}\nAnalyzing {ticker_str}...")
    t = yf.Ticker(ticker_str)
    info = t.info or {}
    result = {"ticker": ticker_str, "name": info.get("longName", ticker_str)}
    
    # 1. Price action (1yr)
    hist = t.history(period="1y")
    if not hist.empty:
        prices = hist["Close"]
        result["price_action"] = {
            "current": round(float(prices.iloc[-1]), 2),
            "1yr_ago": round(float(prices.iloc[0]), 2),
            "1yr_return_pct": round(float((prices.iloc[-1] / prices.iloc[0] - 1) * 100), 2),
            "52wk_high": round(float(prices.max()), 2),
            "52wk_low": round(float(prices.min()), 2),
            "pct_from_52wk_high": round(float((prices.iloc[-1] / prices.max() - 1) * 100), 2),
            "50d_ma": round(float(prices.tail(50).mean()), 2),
            "200d_ma": round(float(prices.tail(200).mean()), 2) if len(prices) >= 200 else None,
        }
        cur = float(prices.iloc[-1])
        ma50 = result["price_action"]["50d_ma"]
        ma200 = result["price_action"]["200d_ma"]
        result["price_action"]["above_50d_ma"] = cur > ma50
        result["price_action"]["above_200d_ma"] = cur > ma200 if ma200 else None
    
    # 2. Insider activity
    try:
        insiders = t.insider_transactions
        if insiders is not None and not insiders.empty:
            recent = insiders.head(10)
            txns = []
            for _, row in recent.iterrows():
                txns.append({
                    "date": str(row.get("Start Date", "")),
                    "insider": str(row.get("Insider", "")),
                    "transaction": str(row.get("Transaction", "")),
                    "shares": str(row.get("Shares", "")),
                    "value": str(row.get("Value", "")),
                })
            result["insider_transactions"] = txns
        else:
            result["insider_transactions"] = []
    except:
        result["insider_transactions"] = []

    # 3. Institutional ownership
    try:
        inst = t.institutional_holders
        if inst is not None and not inst.empty:
            top5 = []
            for _, row in inst.head(5).iterrows():
                top5.append({
                    "holder": str(row.get("Holder", "")),
                    "shares": int(row.get("Shares", 0)) if row.get("Shares") else 0,
                    "pct_held": str(row.get("pctHeld", "")),
                })
            result["top_institutional_holders"] = top5
        result["institutional_pct"] = info.get("heldPercentInstitutions")
    except:
        result["top_institutional_holders"] = []

    # 4. Earnings surprises
    try:
        earn = t.earnings_dates
        if earn is not None and not earn.empty:
            surprises = []
            for idx, row in earn.head(8).iterrows():
                s = {
                    "date": str(idx),
                    "eps_estimate": row.get("EPS Estimate"),
                    "eps_actual": row.get("Reported EPS"),
                    "surprise_pct": row.get("Surprise(%)"),
                }
                # clean NaN
                s = {k: (None if isinstance(v, float) and np.isnan(v) else v) for k, v in s.items()}
                surprises.append(s)
            result["earnings"] = surprises
        else:
            result["earnings"] = []
    except:
        result["earnings"] = []

    # 5. Analyst consensus
    result["analyst"] = {
        "target_mean": info.get("targetMeanPrice"),
        "target_low": info.get("targetLowPrice"),
        "target_high": info.get("targetHighPrice"),
        "recommendation": info.get("recommendationKey"),
        "num_analysts": info.get("numberOfAnalystOpinions"),
    }
    if result["price_action"].get("current") and info.get("targetMeanPrice"):
        upside = (info["targetMeanPrice"] / result["price_action"]["current"] - 1) * 100
        result["analyst"]["upside_pct"] = round(upside, 2)

    # 6. Key fundamentals snapshot
    result["fundamentals"] = {
        "trailing_pe": info.get("trailingPE"),
        "forward_pe": info.get("forwardPE"),
        "peg_ratio": info.get("pegRatio"),
        "market_cap": info.get("marketCap"),
        "revenue_growth": info.get("revenueGrowth"),
        "earnings_growth": info.get("earningsGrowth"),
        "roe": info.get("returnOnEquity"),
        "debt_to_equity": info.get("debtToEquity"),
        "dividend_yield": info.get("dividendYield"),
        "beta": info.get("beta"),
    }

    # 7. Technical - RSI approximation
    if not hist.empty and len(hist) >= 14:
        delta = hist["Close"].diff()
        gain = delta.where(delta > 0, 0).rolling(14).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
        rs = gain / loss
        rsi = 100 - (100 / (1 + rs))
        result["technical"] = {
            "rsi_14": round(float(rsi.iloc[-1]), 2) if not np.isnan(rsi.iloc[-1]) else None,
            "trend": "bullish" if cur > ma50 and (ma200 is None or cur > ma200) else "bearish" if cur < ma50 and (ma200 and cur < ma200) else "mixed",
        }

    print(f"  Done: {result['name']} @ ${result['price_action']['current']}")
    return result

# ============================================================
# EXPANDED GARP SCAN
# ============================================================
def get_broad_ticker_list():
    """Get a broad list of US tickers to screen."""
    import urllib.request
    # Use Wikipedia's S&P 500 + S&P 400 midcap for broader coverage
    # Plus some known Russell 1000 members not in S&P 500
    
    # Start with S&P 500
    sp500 = []
    try:
        import pandas as pd
        tables = pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies")
        sp500 = tables[0]["Symbol"].str.replace(".", "-", regex=False).tolist()
    except:
        pass
    
    # S&P 400 midcap
    sp400 = []
    try:
        import pandas as pd
        tables = pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_400_companies")
        sp400 = tables[0]["Symbol"].str.replace(".", "-", regex=False).tolist()
    except:
        pass
    
    all_tickers = list(set(sp500 + sp400))
    print(f"Total tickers to screen: {len(all_tickers)}")
    return all_tickers

def garp_screen(tickers, exclude=None):
    """Apply GARP filters to ticker list."""
    exclude = set(exclude or [])
    passed = []
    
    for i, tick in enumerate(tickers):
        if tick in exclude:
            continue
        if i % 50 == 0:
            print(f"  Screening {i}/{len(tickers)}...")
        try:
            t = yf.Ticker(tick)
            info = t.info or {}
            
            mc = info.get("marketCap", 0) or 0
            if mc < 5e9:
                continue
            
            tpe = info.get("trailingPE")
            if tpe is None or tpe >= 25 or tpe <= 0:
                continue
                
            fpe = info.get("forwardPE")
            if fpe is None or fpe >= 15 or fpe <= 0:
                continue
            
            rg = info.get("revenueGrowth")
            if rg is None or rg < 0.10:
                continue
            
            eg = info.get("earningsGrowth")
            if eg is None or eg < 0.15:
                continue
            
            roe = info.get("returnOnEquity")
            if roe is None or roe < 0.05:
                continue
            
            # Optional filters
            peg = info.get("pegRatio")
            if peg is not None and peg > 1.2:
                continue
            
            dte = info.get("debtToEquity")
            if dte is not None and dte > 35:
                continue
            
            passed.append({
                "ticker": tick,
                "name": info.get("longName", tick),
                "market_cap": mc,
                "trailing_pe": round(tpe, 2),
                "forward_pe": round(fpe, 2),
                "peg": round(peg, 2) if peg else None,
                "revenue_growth": round(rg * 100, 1),
                "earnings_growth": round(eg * 100, 1),
                "roe": round(roe * 100, 1),
                "debt_to_equity": round(dte, 1) if dte else None,
            })
            print(f"  ✅ PASS: {tick} (PE:{tpe:.1f} FPE:{fpe:.1f} RG:{rg*100:.0f}% EG:{eg*100:.0f}%)")
            
        except Exception as e:
            continue
    
    return passed

# ============================================================
# MAIN
# ============================================================
if __name__ == "__main__":
    output_dir = Path("/home/wdjones/.openclaw/workspace/data")
    output_dir.mkdir(exist_ok=True)
    
    # Deep dive on 4 stocks
    print("=" * 60)
    print("DEEP DIVE ANALYSIS")
    print("=" * 60)
    
    analyses = {}
    for tick in TARGETS:
        analyses[tick] = analyze_stock(tick)
    
    # Save deep dive
    with open(output_dir / "stock-analysis-deep-dive.json", "w") as f:
        json.dump(analyses, f, indent=2, default=str)
    print(f"\nSaved deep dive to stock-analysis-deep-dive.json")
    
    # Expanded GARP scan
    print("\n" + "=" * 60)
    print("EXPANDED GARP SCAN (S&P 500 + S&P 400 MidCap)")
    print("=" * 60)
    
    tickers = get_broad_ticker_list()
    new_passes = garp_screen(tickers, exclude=set(TARGETS))
    
    with open(output_dir / "garp-expanded-scan.json", "w") as f:
        json.dump(new_passes, f, indent=2, default=str)
    print(f"\nExpanded scan found {len(new_passes)} additional stocks")
    print("Saved to garp-expanded-scan.json")
    
    # Print summary
    print("\n" + "=" * 60)
    print("SUMMARY")
    print("=" * 60)
    for tick, data in analyses.items():
        pa = data.get("price_action", {})
        an = data.get("analyst", {})
        fu = data.get("fundamentals", {})
        te = data.get("technical", {})
        print(f"\n{tick} - {data['name']}")
        print(f"  Price: ${pa.get('current')} | 1yr Return: {pa.get('1yr_return_pct')}%")
        print(f"  From 52wk High: {pa.get('pct_from_52wk_high')}%")
        print(f"  PE: {fu.get('trailing_pe')} | Fwd PE: {fu.get('forward_pe')} | PEG: {fu.get('peg_ratio')}")
        print(f"  Target: ${an.get('target_mean')} ({an.get('upside_pct')}% upside) | Rec: {an.get('recommendation')}")
        print(f"  RSI: {te.get('rsi_14')} | Trend: {te.get('trend')}")
        print(f"  Insiders: {len(data.get('insider_transactions', []))} recent txns")
    
    if new_passes:
        print(f"\nNew GARP Candidates ({len(new_passes)}):")
        for s in sorted(new_passes, key=lambda x: x.get("forward_pe", 99)):
            print(f"  {s['ticker']:6s} PE:{s['trailing_pe']:5.1f} FPE:{s['forward_pe']:5.1f} RG:{s['revenue_growth']:5.1f}% EG:{s['earnings_growth']:5.1f}% ROE:{s['roe']:5.1f}%")
