from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.encoders import jsonable_encoder
from bson import ObjectId
import numpy as np
from typing import List, Optional
from app.db import database
from app.v1.services.zerodha.client import ZerodhaClient
from app.v1.services.zerodha.engine import TradingEngine
from app.v1.services.zerodha.db_manager import MongoDBManager
from app.v1.dependencies.auth import get_current_userdetails
from app.v1.services.zerolive.strategy import BearishDivergenceStrategy
from app.v1.services.zerolive.bearish import BearishSellDivergenceStrategy
from app.v1.services.zerolive.list import get_top_gainer_symbols, get_top_loser_symbols  # ✅ import from your list.py
import logging
from datetime import datetime
from enum import Enum

import os
import logging

router = APIRouter()
logger = logging.getLogger(__name__)

class MoverType(str, Enum):
    gainers = "gainers"
    losers  = "losers"

# Use injected DB instance - Not required here, but for consistency
def get_zerodha_client():
    api_key = os.getenv("ZERODHA_API_KEY", "")
    api_secret = os.getenv("ZERODHA_API_SECRET", "")
    access_token = os.getenv("ZERODHA_ACCESS_TOKEN", "")
    return ZerodhaClient(api_key, api_secret, access_token)

def sanitize_numpy(obj):
    if isinstance(obj, dict):
        return {k: sanitize_numpy(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [sanitize_numpy(i) for i in obj]
    elif isinstance(obj, (np.integer, np.int32, np.int64)):
        return int(obj)
    elif isinstance(obj, (np.floating, np.float32, np.float64)):
        return float(obj)
    elif isinstance(obj, ObjectId):
        return str(obj)
    return obj

def get_ema_status(signal_data: dict, timeframe: str) -> str:
    """
    Get EMA status for a specific timeframe
    """
    ema_stack = signal_data.get("ema_stack", {})
    if not ema_stack:
        return "No EMA data"
    
    # Check if all EMAs are in bullish order
    ema_periods = [5, 9, 15, 21, 30, 55, 100, 200]
    ema_values = [ema_stack.get(f"ema_{period}", None) for period in ema_periods]
    
    if all(ema_values[i] > ema_values[i + 1] for i in range(len(ema_values) - 1)):
        return "Bullish"
    elif all(ema_values[i] < ema_values[i + 1] for i in range(len(ema_values) - 1)):
        return "Bearish"
    
    return "Mixed"

def format_ui_signal(signal_data: dict) -> dict:
    """Enhance signal data for UI presentation"""
    return {
        "symbol": signal_data["symbol"],
        "decision": signal_data["decision"],
        "confidence": signal_data["confidence_score"],
        "timeframes": {
            "30min": {
                "pivot": signal_data["pivots"].get("30min", {}),
                "ema_status": get_ema_status(signal_data, "30min")
            },
            "daily": {
                "pivot": signal_data["pivots"].get("day", {}),
                "ema_status": get_ema_status(signal_data, "day")
            },
            "weekly": {
                "pivot": signal_data["pivots"].get("week", {}),
                "ema_status": get_ema_status(signal_data, "week")
            },
            "monthly": {
                "pivot": signal_data["pivots"].get("month", {}),
                "ema_status": get_ema_status(signal_data, "month")
            }
        },
        "key_levels": {
            "yearly_high": signal_data.get("yearly_high", 0),
            "yearly_low": signal_data.get("yearly_low", 0),
            "circuit_limits": {
                "upper": signal_data.get("yearly_high", 0),
                "lower": signal_data.get("yearly_low", 0)
            }
        },
        "reason": signal_data.get("reason", ""),
        "timestamp": signal_data["timestamp"]
    }

def get_zerodha_client_from_db(
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails)
) -> ZerodhaClient:
    user_id = str(current_user.get("_id"))
    settings = db["zerodha_settings"].find_one({"user_id": user_id})
    print(settings)

    if not settings:
        raise HTTPException(status_code=404, detail="Zerodha settings not found for user")

    return ZerodhaClient(
        api_key=settings["api_key"],
        api_secret=settings["api_secret"],
        access_token=settings.get("access_token", "")  # Optional, if not yet connected
    )


@router.get("/signals", summary="Get signals for all user portfolios")
async def get_all_portfolio_signals(
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
    zerodha: ZerodhaClient = Depends(get_zerodha_client_from_db)
):
    try:
        user_id = str(current_user.get("_id"))
        db_manager = MongoDBManager(db)  # << CORRECT way
        engine = TradingEngine(zerodha, db_manager)

        portfolios = db["portfolio"].find({"user_id": user_id})
        signals = []
        for portfolio in portfolios:
            symbol = portfolio.get("symbol")
            if symbol:
                signal = engine.analyze_symbol(symbol)
                signals.append(signal)

        return {
            "status": "success",
            "user_id": user_id,
            "signals_generated": len(signals),
            "signals": signals
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/signals/{portfolio}")
async def get_single_portfolio_signal(
    portfolio: str,
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
    zerodha: ZerodhaClient = Depends(get_zerodha_client_from_db)
):
    try:
        user_id = str(current_user.get("_id"))
        db_manager = MongoDBManager(db)
        engine = TradingEngine(zerodha, db_manager)
        print(f"Fetching signal for portfolio: {engine} for user: {user_id}")

        symbol = portfolio 

        signal = engine.analyze_symbol(symbol)
        print(f"Signal for {symbol}: {signal}")

        return {
            "status": "success",
            "portfolio": portfolio,
            "symbol": symbol,
            "signal": sanitize_numpy(signal)
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/callback")
def post_callback(
    payload: dict,
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails)
):
    request_token = payload.get("request_token")
    user_id = str(current_user.get("_id"))

    if not request_token:
        raise HTTPException(status_code=400, detail="Missing request token")

    settings = db["zerodha_settings"].find_one({"user_id": user_id})
    if not settings:
        raise HTTPException(status_code=404, detail="Zerodha settings not found")

    client = ZerodhaClient(settings["api_key"], settings["api_secret"])
    session = client.generate_session(request_token)

    db["zerodha_settings"].update_one(
        {"user_id": user_id},
        {"$set": {"access_token": session["access_token"]}}
    )

    return {"status": "connected"}

@router.post("/postback")
async def kite_postback(payload: dict):
    try:
        # Zerodha sends JSON postback with order status
        # Save to DB, trigger alerts, etc.
        logging.info("Zerodha postback received: %s", payload)
        database.get_mongo_db()["zerodha_postbacks"].insert_one(payload)
        return {"status": "received"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/profile")
def get_zerodha_profile(
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails)
):
    settings = db["zerodha_settings"].find_one({"user_id": str(current_user["_id"])})
    if not settings or not settings.get("access_token"):
        raise HTTPException(status_code=403, detail="Not authenticated with Zerodha")

    client = ZerodhaClient(settings["api_key"], settings["api_secret"], settings["access_token"])
    try:
        profile = client.get_profile()
        return {"status": "ok", "profile": profile}
    except Exception:
        raise HTTPException(status_code=403, detail="Invalid/expired Zerodha access token")


@router.get("/live")
async def run_live_strategy(
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
    zerodha: ZerodhaClient = Depends(get_zerodha_client_from_db),
    mover: MoverType = Query(
            MoverType.gainers,
            description="Which top movers to run the strategy on: gainers or losers"
        )

):
    user_id = current_user.get("_id")
    logger.info(f"Running  {mover}  live strategy for user: {user_id}")

    # 1) Fetch symbols based on mover param
    if mover == MoverType.gainers:
        symbols = get_top_gainer_symbols(db)
    else:
        symbols = get_top_loser_symbols(db)
    
    if not symbols:
        logger.info("No valid symbols found")
        return {"status": "success", "message": "No valid symbols found", "results": []}

    try:
        strategy = BearishDivergenceStrategy(
            api_key=zerodha.api_key,
            api_secret=zerodha.api_secret,
            access_token=zerodha.access_token,
            db=db
        )
        raw = strategy.run_strategy(symbols)
        #print(f"Raw strategy results: {raw}")

        # 2) Prepare analytics docs
        analytics_docs = []
        for r in raw:
            analytics_docs.append({
                "timestamp":    datetime.utcnow(),
                "user_id":      user_id,
                "symbol":       r["symbol"],
                "features": {
                    "volume_drop":      r["volume_drop"],
                    "ema5_gap":         r["ema5_gap"],
                    "bear_divergence":  r["divergence_confirmed"],
                    "bull_divergence":  r["divergence_bull"],
                    "near_resistance":  r["near_resistance"],
                    "near_support":     r["near_support"],
                },
                "metrics": {
                    "bear_score": r["bear_score"],
                    "bull_score": r["bull_score"],
                    "decision":   r["decision"]
                },
                "targets": {
                    "entry": r["entry_price"],
                    "exit":  r["target_price"]
                },
                "outcome":        None,  # to be filled later by backtest
                "prediction":     None,  # to be filled later by ML
                "user_feedback":  None   # to capture thumbs up/down
            })

        # 3) Bulk-insert analytics
        ids = []
        if analytics_docs:
            inserted = db["stream"].insert_many(analytics_docs)
            ids = inserted.inserted_ids

        # 4) Shape the response exactly for the frontend
        results = []
        for idx, r in zip(ids, raw):
            results.append({
                "id":               str(idx),
                "symbol":           r["symbol"],
                "instrument_token": r["instrument_token"],
                "metrics": {
                    # bearish
                    "volume_drop":     r["volume_drop"],
                    "ema_gap":         r["ema5_gap"],
                    "divergence":      r["divergence_confirmed"],
                    "near_resistance": r["near_resistance"],
                    "bear_score":      r["bear_score"],

                    # bullish
                    "volume_rise":     r["volume_rise"],
                    "ema_gap_bull":    r["ema5_gap_bull"],
                    "divergence_bull": r["divergence_bull"],
                    "near_support":    r["near_support"],
                    "bull_score":      r["bull_score"],

                    # final decision
                    "decision":        r["decision"],
                },
                "targets": {
                    "entry": r["entry_price"],
                    "exit":  r["target_price"]
                }
            })

        return {"status": "success", "scanned": len(symbols), "results": results}

    except Exception as e:
        logger.error(f"Strategy execution failed for user {user_id}: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail="Strategy execution failed")

@router.get("/shortsells", summary="Run intraday short-sell strategy")
async def run_intraday_shorts(
    mover: str = Query("gainers", description="Run on 'gainers' or 'losers' list"),
    capital: float = Query(100000.0, description="Base capital in ₹"),
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
    zerodha=Depends(get_zerodha_client_from_db),
):
    # 1) Universe
    symbols = (
        get_top_gainer_symbols(db)
        if mover == "gainers"
        else get_top_loser_symbols(db)
    )
    if not symbols:
        return {"status": "success", "message": "No symbols to scan", "results": []}

    # 2) Run strategy
    strategy = BearishSellDivergenceStrategy(
        api_key=zerodha.api_key,
        api_secret=zerodha.api_secret,
        access_token=zerodha.access_token,
        db=db,
        capital=capital,
    )
    raw = strategy.run_strategy(symbols)

    # 3) Shape response
    results = []
    for idx, r in enumerate(raw, start=1):
        try:
            # a) pull & cast prices
            sell_entry   = float(r["entry_price"])
            cover_target = float(r["target_price"])
            qty          = int(r["quantity"])
            expected_pnl = round((sell_entry - cover_target) * qty, 2)

            # b) pivot & upper-circuit come straight from r
            nearest  = r.get("pivot_level", "")
            upper_ok = bool(r.get("near_upper_circuit", False))

            # c) metrics – note these exact keys match your React table
            volume_drop   = float(r.get("volume_drop_pct", 0.0))
            momentum_drop = float(r.get("momentum_drop_pct", 0.0))
            ema_gap       = float(r.get("ema5_gap_pct", 0.0))

            metrics = {
                "volume_drop":        volume_drop,
                "momentum_drop":      momentum_drop,
                "ema_gap":            ema_gap,
                "rsi_divergence":     bool(r.get("rsi_divergence", False)),
                "wedge":              bool(r.get("rising_wedge", False)),
                "pivot_level":        nearest,
                "near_upper_circuit": upper_ok,
            }

            # d) assemble top-level so React sees sig.sell_entry, sig.cover_target, sig.expected_profit
            results.append({
                "rank":             idx,
                "symbol":           r["symbol"],
                "instrument_token": r["instrument_token"],
                "sell_entry":       sell_entry,
                "cover_target":     cover_target,
                "quantity":         qty,
                "expected_profit":  expected_pnl,
                "metrics":          metrics,
                "skip_reasons":     r.get("skip_reasons", []),
            })

        except Exception as e:
            logger.error(f"Error shaping result for {r.get('symbol')}: {e}", exc_info=True)
            continue

    return {
        "status":  "success",
        "scanned": len(symbols),
        "results": results
    }


@router.post("/stream/feedback/{id}")
def feedback(id: str, payload: dict, db=Depends(database.get_mongo_db),):
    fb = payload.get("feedback")  # 'up' or 'down'
    db["stream"].update_one(
        {"_id": ObjectId(id)},
        {"$set": {"user_feedback": fb}}
    )
    return {"status": "ok"}
