"""Watchlist endpoints (spec §39).

AUDIT FIX (StockLab overhaul, final engineering pass, Part A3, docs/AUDIT_SECURITY_A3.md):
the two write routes (add/remove) are rate-limited -- both are authenticated and scoped to the
caller's own watchlist (no cross-user blast radius), but each does a real DB write + commit, so a
scripted caller can still generate meaningful write/commit churn
a generous, configurable limit
covers that without throttling normal use. GET (list) is deliberately left undecorated: it's
authenticated, read-only, and its cost no longer scales per-item after the Part A2 batching fix
above -- rate limiting a normal "load my watchlist" page view would add friction with no real risk
being mitigated.
"""

from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.api.v1.deps import get_current_user, limiter
from app.api.v1.serializers import (
    company_summary, is_demo_by_security, latest_scores_by_security, latest_valuations_by_security,
    security_eager_load_options,
)
from app.core.config import get_settings
from app.core.db import get_db
from app.models import Security, User, Watchlist, WatchlistItem

router = APIRouter(prefix="/v1/watchlist", tags=["watchlist"])

_WATCHLIST_WRITE_RATE_LIMIT = f"{get_settings().WATCHLIST_WRITE_RATE_LIMIT_PER_MINUTE}/minute"


def _get_or_create_default_watchlist(db: Session, user: User) -> Watchlist:
    wl = db.query(Watchlist).filter_by(owner_user_id=user.id).first()
    if wl is None:
        wl = Watchlist(owner_user_id=user.id)
        db.add(wl)
        db.commit()
        db.refresh(wl)
    return wl


@router.get("")
def get_watchlist(db: Session = Depends(get_db), user: User = Depends(get_current_user)):
    # AUDIT FIX (StockLab overhaul, Part A2, docs/AUDIT_PERFORMANCE.md's remaining
    # company_summary() N+1 finding): this endpoint used to run one Security get, one Score query,
    # one Valuation query, plus company_summary()'s own company/country/sector/industry/is_demo
    # lazy loads -- PER watchlist item (up to ~7N queries for N items). All four lookups are now
    # batched the same way screeners.py/rankings.py already were (Score/Valuation from the earlier
    # performance pass; Security eager-load and is_demo from this one).
    wl = _get_or_create_default_watchlist(db, user)
    items = db.query(WatchlistItem).filter_by(watchlist_id=wl.id).all()
    security_ids = [item.security_id for item in items]
    if not security_ids:
        return []

    securities_by_id = {
        sec.id: sec
        for sec in db.execute(
            select(Security).where(Security.id.in_(security_ids)).options(*security_eager_load_options())
        ).scalars().all()
    }
    scores_by_id = latest_scores_by_security(db, security_ids)
    valuations_by_id = latest_valuations_by_security(db, security_ids)
    demo_by_id = is_demo_by_security(db, security_ids)

    out = []
    for item in items:
        sec = securities_by_id.get(item.security_id)
        if sec is None:
            # Security row no longer exists (e.g. deleted after being watchlisted) -- skip rather
            # than crash; the original per-item db.get() would have raised AttributeError on the
            # very next line in this same situation, so this is strictly more robust, not a change
            # in output for any watchlist item that still resolves to a real Security.
            continue
        score = scores_by_id.get(sec.id)
        val = valuations_by_id.get(sec.id)
        out.append({
            "company": company_summary(db, sec, is_demo=demo_by_id.get(sec.id, False)).model_dump(),
            "overall_score": score.overall_score if score else None,
            "recommendation": score.recommendation if score else None,
            "price": val.price_at_calculation if val else None,
            "fair_value": val.weighted_fair_value if val else None,
            "margin_of_safety": val.margin_of_safety if val else None,
            "note": item.note,
        })
    return out


@router.post("/{ticker}", status_code=201)
@limiter.limit(_WATCHLIST_WRITE_RATE_LIMIT)
def add_to_watchlist(request: Request, ticker: str, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
    security = db.query(Security).filter_by(ticker=ticker.upper()).first()
    if security is None:
        raise HTTPException(404, f"No security for ticker {ticker}")
    wl = _get_or_create_default_watchlist(db, user)
    existing = db.query(WatchlistItem).filter_by(watchlist_id=wl.id, security_id=security.id).first()
    if existing:
        return {"status": "already_in_watchlist"}
    db.add(WatchlistItem(watchlist_id=wl.id, security_id=security.id))
    db.commit()
    return {"status": "added"}


@router.delete("/{ticker}", status_code=204)
@limiter.limit(_WATCHLIST_WRITE_RATE_LIMIT)
def remove_from_watchlist(request: Request, ticker: str, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
    security = db.query(Security).filter_by(ticker=ticker.upper()).first()
    if security is None:
        raise HTTPException(404, f"No security for ticker {ticker}")
    wl = _get_or_create_default_watchlist(db, user)
    db.query(WatchlistItem).filter_by(watchlist_id=wl.id, security_id=security.id).delete()
    db.commit()
