"""Build a normalized portfolio snapshot from Interactive Brokers."""

from __future__ import annotations

from collections import defaultdict
from datetime import datetime
from typing import Any
from zoneinfo import ZoneInfo

from RenTech.live.protocols import PortfolioSnapshot, PositionLeg

NY = ZoneInfo("America/New_York")

_ACCOUNT_TAGS = (
    "NetLiquidation",
    "AvailableFunds",
    "ExcessLiquidity",
    "MaintMarginReq",
    "UnrealizedPnL",
    "RealizedPnL",
)


async def fetch_portfolio_snapshot(ib: Any) -> PortfolioSnapshot:
    tags: dict[str, float] = {}
    vals = await ib.accountSummaryAsync()
    for av in vals:
        if av.currency not in ("USD", "BASE"):
            continue
        if av.tag in _ACCOUNT_TAGS:
            try:
                tags[av.tag] = float(av.value)
            except (TypeError, ValueError):
                pass

    net = tags.get("NetLiquidation", 0.0)
    avail = tags.get("AvailableFunds", 0.0)
    excess = tags.get("ExcessLiquidity", 0.0)
    maint = tags.get("MaintMarginReq", 0.0)
    upnl = tags.get("UnrealizedPnL", 0.0)
    rpnl = tags.get("RealizedPnL", 0.0)

    if net <= 0:
        raise RuntimeError("NetLiquidation (USD) missing or non-positive from IB account summary")

    legs: list[PositionLeg] = []
    for p in ib.positions():
        c = p.contract
        sym = str(getattr(c, "symbol", "") or "")
        sec = str(getattr(c, "secType", "") or "")
        exp = str(getattr(c, "lastTradeDateOrContractMonth", "") or "")
        strike = float(getattr(c, "strike", 0) or 0)
        right = str(getattr(c, "right", "") or "")
        pos = float(p.position)
        if abs(pos) < 1e-9:
            continue
        avg = float(p.avgCost or 0)
        mv = float(getattr(p, "marketValue", 0) or 0)
        upl = float(getattr(p, "unrealizedPNL", 0) or getattr(p, "unrealizedPnL", 0) or 0)
        legs.append(
            PositionLeg(
                con_id=int(c.conId),
                symbol=sym,
                sec_type=sec,
                expiry=exp,
                strike=strike,
                right=right,
                position=pos,
                avg_cost=avg,
                market_value=mv,
                unrealized_pnl=upl,
            )
        )

    by_sym: dict[str, list[PositionLeg]] = defaultdict(list)
    for leg in legs:
        by_sym[leg.symbol].append(leg)

    margin_util = (maint / net) if net > 0 and maint > 0 else 0.0

    return PortfolioSnapshot(
        as_of=datetime.now(NY),
        net_liquidation_usd=net,
        available_funds_usd=avail,
        excess_liquidity_usd=excess,
        maintenance_margin_usd=maint,
        unrealized_pnl_usd=upnl,
        realized_pnl_today_usd=rpnl,
        margin_utilization=float(margin_util),
        positions=tuple(legs),
        positions_by_symbol={k: tuple(v) for k, v in by_sym.items()},
        raw_account_tags=tags,
    )
