"""Libri REALI per strumento dai status.json persistiti dei worker — fonte unica.

Usato da:
  - scripts/analysis/reconcile_account.py (audit orario conto vs libri)
  - ExecutionClient.close_amount (guard del netting: il residuo non-reduce-only
    e' consentito solo fino al gap conto-vs-libri-altrui — senza questo guard un
    close su stato stantio APRIREBBE una posizione nuda, code-review 2026-06-11)

Convenzione: amount firmato in base-coin (buy=+, sell=-), strumenti USDC lineari.
"""
from __future__ import annotations

import json
from pathlib import Path

PROJECT_ROOT = Path(__file__).resolve().parents[2]
PAPER = PROJECT_ROOT / "data" / "portfolio_paper"


def _inst(asset: str) -> str:
    return f"{asset}_USDC-PERPETUAL"


def real_books(exclude_worker: str | None = None) -> tuple[dict[str, float], dict[str, float]]:
    """(libri per strumento, orfani registrati per strumento), firmati.

    exclude_worker: worker_id da ESCLUDERE dai libri (per il guard del netting:
    il close del worker X deve portare il conto al netto degli ALTRI). Gli
    orphan_legs NON vengono mai esclusi: sono posizioni che il conto ha ancora
    indipendentemente da chi le ha originate."""
    books: dict[str, float] = {}
    orphans: dict[str, float] = {}
    for sp in sorted(PAPER.glob("*/status.json")):
        wid = sp.parent.name
        try:
            st = json.loads(sp.read_text())
        except Exception:
            continue
        if st.get("real_in_position") and wid != exclude_worker:
            parts = wid.split("__")
            if st.get("real_amount_a"):
                a, b = parts[1].split("_")          # pairs: …__ETH_BTC__1h
                for asset, side, amt in ((a, st["real_side_a"], st["real_amount_a"]),
                                         (b, st["real_side_b"], st["real_amount_b"])):
                    sgn = 1 if side == "buy" else -1
                    books[_inst(asset)] = books.get(_inst(asset), 0.0) + sgn * amt
            elif st.get("real_amount"):
                sgn = 1 if st.get("real_side") == "buy" else -1
                books[_inst(parts[1])] = books.get(_inst(parts[1]), 0.0) + sgn * st["real_amount"]
        for o in st.get("orphan_legs", []):
            sgn = 1 if o.get("entry_side") == "buy" else -1
            orphans[o["instrument"]] = orphans.get(o["instrument"], 0.0) + sgn * float(o["amount"])
    return books, orphans


def expected_resting() -> list[dict]:
    """Ordini resting ATTESI sul book dai libri dei worker single-leg in posizione
    reale: TP limit reduce-only (real_tp_order_id) e disaster-SL stop_market
    (real_dsl_order_id). I pairs non hanno resting. Ogni voce:
    {worker, instrument, order_id, kind: 'tp'|'dsl'}."""
    out: list[dict] = []
    for sp in sorted(PAPER.glob("*/status.json")):
        wid = sp.parent.name
        try:
            st = json.loads(sp.read_text())
        except Exception:
            continue
        if not st.get("real_in_position") or st.get("real_amount_a"):
            continue
        inst = _inst(wid.split("__")[1])
        for key, kind in (("real_tp_order_id", "tp"), ("real_dsl_order_id", "dsl")):
            oid = st.get(key)
            if oid:
                out.append(dict(worker=wid, instrument=inst, order_id=str(oid), kind=kind))
    return out


def account_net(client) -> dict[str, float]:
    """Posizioni reali per strumento dal conto (size USD / mark -> coin, firmato)."""
    out: dict[str, float] = {}
    for p in client.get_positions(currency="USDC") or []:
        inst = p.get("instrument")
        size = float(p.get("size") or 0)
        mark = float(p.get("mark_price") or 0)
        if not inst or not size or not mark:
            continue
        amt = size / mark
        out[inst] = amt if p.get("direction") == "long" else -amt
    return out
