"""
=========================================
CV6 AI Trading OS
Position guard — Phase 2.5 (Goal 5)

Enforces "one symbol = one position" for the manual/execution order
paths (/broker/orders/place, /execution/place-order,
/execution/execute-trade), which — unlike the autonomous engine — keep
no local position ledger of their own. The only source of truth
available to these paths is the broker's own live position book, so
this checks that directly before allowing an order that would open or
add to an existing position.

A SELL against an existing long (or a BUY against an existing short) is
correctly allowed through — it reduces/closes the position rather than
duplicating it.
"""

from typing import Any, Optional

from loguru import logger


def get_net_position_qty(broker, symbol: str, exchange: str = "") -> Optional[int]:
    """
    Returns the signed net quantity currently held for `symbol` at the
    broker (positive = net long, negative = net short), or None if there
    is no open position / the lookup failed (fail-open: an unknown
    position state must never block a legitimate order).
    """
    try:
        raw = broker.positions()
    except Exception as e:
        logger.warning(f"[PositionGuard] positions() lookup failed: {e}")
        return None

    if isinstance(raw, dict):
        if raw.get("success") is False:
            return None
        pos_list = raw.get("data")
        if not isinstance(pos_list, list):
            pos_list = raw.get("positions") if isinstance(raw.get("positions"), list) else []
    elif isinstance(raw, list):
        pos_list = raw
    else:
        pos_list = []

    net = 0
    found = False
    sym_u = symbol.upper()
    exch_u = exchange.upper() if exchange else ""
    for p in pos_list:
        psym = str(p.get("tradingsymbol") or p.get("symbol", "")).upper()
        if psym != sym_u:
            continue
        pexch = str(p.get("exchange", "")).upper()
        if exch_u and pexch and pexch != exch_u:
            continue
        qty = p.get("netqty")
        if qty is None:
            qty = p.get("quantity", 0)
        try:
            qty = int(float(qty))
        except (TypeError, ValueError):
            continue
        net += qty
        found = True

    return net if found else None


def blocks_new_entry(existing_net_qty: Optional[int], new_side: str) -> bool:
    """
    True if placing `new_side` would ADD to an existing position rather
    than reduce/close it — i.e. violates one-symbol-one-position.
    """
    if not existing_net_qty:
        return False
    side = (new_side or "").upper()
    if existing_net_qty > 0 and side == "BUY":
        return True   # already net long — another BUY pyramids it
    if existing_net_qty < 0 and side == "SELL":
        return True   # already net short — another SELL pyramids it
    return False


def describe_existing(existing_net_qty: int) -> str:
    direction = "LONG" if existing_net_qty > 0 else "SHORT"
    return f"{direction} {abs(existing_net_qty)}"
