"""Portfolio routes for platform teGPT.

Reused as a simplified DAILY personal watchlist.

Rules:
- Max 10 ACTIVE symbols per user per IST day
- User can add/remove anytime
- Automatically "flushes" daily by scoping to today's IST date
- Also syncs into the global intraday watchlist (source=MANUAL)
"""

from fastapi import APIRouter, Body, Depends, HTTPException, Query
from typing import Any, Dict, List, Optional
import logging
from datetime import datetime

from bson import ObjectId

from app.db import database
from app.v1.dependencies.auth import get_current_userdetails
from app.v1.services.intraday_watchlist import (
    SOURCE_MANUAL,
    ist_date_str,
    remove_source_for_symbols,
    upsert_symbols,
)

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


MAX_WATCHLIST_ITEMS_PER_USER = 10


def _norm_symbol(symbol: Any) -> str:
    return (str(symbol or "").strip().upper())


def _norm_tags(tags: Any) -> List[str]:
    if tags is None:
        return []
    if isinstance(tags, str):
        tags = [tags]
    if not isinstance(tags, list):
        return []
    out: List[str] = []
    for t in tags:
        if not isinstance(t, str):
            continue
        s = t.strip().lower()
        if s:
            out.append(s)
    return list(dict.fromkeys(out))


def _get_stock_or_400(db, symbol: str, exchange: str = "NSE") -> Dict[str, Any]:
    sym = _norm_symbol(symbol)
    ex = (exchange or "NSE").strip().upper()
    if not sym:
        raise HTTPException(status_code=400, detail="symbol is required")

    stock = db["stocks"].find_one({"symbol": sym, "exchange": ex}) or db["stocks"].find_one({"symbol": sym})
    if not isinstance(stock, dict):
        raise HTTPException(status_code=400, detail="symbol not found in stocks master; refresh instruments")
    if not stock.get("stock_id"):
        raise HTTPException(status_code=400, detail="stock_id missing for symbol; refresh instruments")
    if stock.get("instrument_token") is None:
        raise HTTPException(status_code=400, detail="instrument_token missing for symbol; refresh instruments")
    return stock


# ============ PERSONAL WATCHLIST (REUSES PORTFOLIO STORAGE) ============


@router.post("/portfolio/items", summary="Add/Upsert watchlist item (daily)")
async def upsert_portfolio_item(
    payload: Dict[str, Any] = Body(...),
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
):
    user_id = str(current_user.get("_id"))
    ist_date = ist_date_str()
    symbol = _norm_symbol(payload.get("symbol"))
    exchange = (payload.get("exchange") or "NSE").strip().upper()
    tags = _norm_tags(payload.get("tags"))

    stock = _get_stock_or_400(db, symbol, exchange=exchange)

    now = datetime.utcnow()

    # Enforce max 10 ACTIVE items for this user for today's IST date.
    active_count = db["user_portfolio_items"].count_documents({"user_id": user_id, "ist_date": ist_date, "status": "ACTIVE"})
    existing = db["user_portfolio_items"].find_one(
        {"user_id": user_id, "stock_id": stock.get("stock_id"), "ist_date": ist_date}
    )
    if not existing and active_count >= MAX_WATCHLIST_ITEMS_PER_USER:
        raise HTTPException(status_code=400, detail=f"Max {MAX_WATCHLIST_ITEMS_PER_USER} watchlist symbols per day")

    doc = {
        "user_id": user_id,
        "stock_id": stock.get("stock_id"),
        "symbol": stock.get("symbol"),
        "exchange": stock.get("exchange") or exchange,
        "status": "ACTIVE",
        "tags": tags,
        "ist_date": ist_date,
        "updated_at": now,
    }

    if existing:
        db["user_portfolio_items"].update_one({"_id": existing["_id"]}, {"$set": doc})
        item_id = str(existing["_id"])
        created_at = existing.get("created_at")
    else:
        inserted = db["user_portfolio_items"].insert_one({**doc, "created_at": now})
        item_id = str(inserted.inserted_id)
        created_at = now

    # Sync to global intraday watchlist (daily) as source=MANUAL.
    try:
        upsert_symbols(
            db,
            ist_date=ist_date,
            symbols=[stock.get("symbol")],
            source=SOURCE_MANUAL,
            meta_by_symbol={stock.get("symbol"): {"tags": tags, "user_id": user_id}},
        )
    except Exception:
        logger.exception("Failed to sync manual watchlist item to intraday watchlist")

    return {
        "status": "ok",
        "item": {
            "portfolio_item_id": item_id,
            "stock_id": stock.get("stock_id"),
            "symbol": stock.get("symbol"),
            "exchange": stock.get("exchange") or exchange,
            "instrument_token": stock.get("instrument_token"),
            "name": stock.get("name"),
            "tags": tags,
            "status": "ACTIVE",
            "ist_date": ist_date,
            "created_at": created_at,
            "updated_at": now,
        },
    }


@router.get("/portfolio/items", summary="List watchlist items (daily)")
async def list_portfolio_items(
    tag: Optional[str] = Query(None, description="Filter by tag"),
    limit: int = Query(50, ge=1, le=200),
    skip: int = Query(0, ge=0),
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
):
    user_id = str(current_user.get("_id"))
    ist_date = ist_date_str()

    query: Dict[str, Any] = {"user_id": user_id, "ist_date": ist_date, "status": "ACTIVE"}
    if tag:
        query["tags"] = (tag or "").strip().lower()

    cur = db["user_portfolio_items"].find(query).sort([("updated_at", -1), ("created_at", -1)]).skip(skip).limit(limit)

    items = list(cur)
    stock_ids = [it.get("stock_id") for it in items if it.get("stock_id")]
    stocks = list(db["stocks"].find({"stock_id": {"$in": stock_ids}}, {"_id": 0, "stock_id": 1, "instrument_token": 1, "name": 1}))
    by_id = {s.get("stock_id"): s for s in stocks if s.get("stock_id")}

    out: List[Dict[str, Any]] = []
    for it in items:
        sid = it.get("stock_id")
        stock = by_id.get(sid) or {}
        out.append(
            {
                "portfolio_item_id": str(it.get("_id")),
                "stock_id": sid,
                "symbol": it.get("symbol"),
                "exchange": it.get("exchange"),
                "instrument_token": stock.get("instrument_token"),
                "name": stock.get("name"),
                "status": it.get("status"),
                "tags": it.get("tags") or [],
                "ist_date": it.get("ist_date"),
                "created_at": it.get("created_at"),
                "updated_at": it.get("updated_at"),
            }
        )

    total = db["user_portfolio_items"].count_documents(query)
    return {"status": "ok", "total": total, "items": out, "ist_date": ist_date}


@router.patch("/portfolio/items/{portfolio_item_id}", summary="Update watchlist tags (daily)")
async def update_portfolio_item(
    portfolio_item_id: str,
    payload: Dict[str, Any] = Body(...),
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
):
    user_id = str(current_user.get("_id"))
    ist_date = ist_date_str()
    try:
        oid = ObjectId(portfolio_item_id)
    except Exception:
        raise HTTPException(status_code=400, detail="Invalid portfolio_item_id")

    existing = db["user_portfolio_items"].find_one({"_id": oid, "user_id": user_id, "ist_date": ist_date})
    if not existing:
        raise HTTPException(status_code=404, detail="Watchlist item not found for today")

    tags = _norm_tags(payload.get("tags"))
    patch: Dict[str, Any] = {"tags": tags, "updated_at": datetime.utcnow(), "status": "ACTIVE"}
    db["user_portfolio_items"].update_one({"_id": oid, "user_id": user_id, "ist_date": ist_date}, {"$set": patch})

    sym = _norm_symbol(existing.get("symbol"))
    if sym:
        try:
            upsert_symbols(db, ist_date=ist_date, symbols=[sym], source=SOURCE_MANUAL, meta_by_symbol={sym: {"tags": tags, "user_id": user_id}})
        except Exception:
            logger.exception("Failed to sync manual watchlist tag update to intraday watchlist")

    updated = db["user_portfolio_items"].find_one({"_id": oid, "user_id": user_id, "ist_date": ist_date}) or {}
    return {"status": "ok", "item": {"portfolio_item_id": str(updated.get("_id")), "stock_id": updated.get("stock_id"), "symbol": updated.get("symbol"), "exchange": updated.get("exchange"), "status": updated.get("status"), "tags": updated.get("tags") or [], "ist_date": updated.get("ist_date"), "created_at": updated.get("created_at"), "updated_at": updated.get("updated_at")}}


@router.delete("/portfolio/items/{portfolio_item_id}", summary="Delete watchlist item (daily)")
async def delete_portfolio_item(
    portfolio_item_id: str,
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
):
    user_id = str(current_user.get("_id"))
    ist_date = ist_date_str()
    try:
        oid = ObjectId(portfolio_item_id)
    except Exception:
        raise HTTPException(status_code=400, detail="Invalid portfolio_item_id")

    existing = db["user_portfolio_items"].find_one({"_id": oid, "user_id": user_id, "ist_date": ist_date})
    if not existing:
        raise HTTPException(status_code=404, detail="Watchlist item not found for today")

    res = db["user_portfolio_items"].delete_one({"_id": oid, "user_id": user_id, "ist_date": ist_date})
    if res.deleted_count != 1:
        raise HTTPException(status_code=404, detail="Watchlist item not found")

    sym = _norm_symbol(existing.get("symbol"))
    if sym:
        try:
            remove_source_for_symbols(db, ist_date=ist_date, symbols=[sym], source=SOURCE_MANUAL)
        except Exception:
            logger.exception("Failed to remove manual watchlist symbol from intraday watchlist")

    return {"status": "ok", "deleted": True}


@router.delete("/portfolio/items/by-symbol/{symbol}")
async def delete_portfolio_item_by_symbol(
    symbol: str,
    db=Depends(database.get_mongo_db),
    current_user=Depends(get_current_userdetails),
):
    user_id = str(current_user.get("_id"))
    ist_date = ist_date_str()
    sym = (symbol or "").strip().upper()
    if not sym:
        raise HTTPException(status_code=400, detail="Missing symbol")

    doc = db["user_portfolio_items"].find_one(
        {"user_id": user_id, "ist_date": ist_date, "symbol": sym, "status": "ACTIVE"},
        {"_id": 1, "symbol": 1},
    )
    if not doc:
        return {"status": "success", "deleted": False}

    db["user_portfolio_items"].update_one(
        {"_id": doc.get("_id")},
        {"$set": {"status": "DELETED", "deleted_at": datetime.utcnow()}},
    )

    try:
        sym_list = [sym]
        remove_source_for_symbols(db, symbols=sym_list, source="MANUAL", ist_date=ist_date)
    except Exception:
        logger.exception("Failed to remove MANUAL source for %s", sym)

    return {"status": "success", "deleted": True}

