"""The ``ctx`` object the engine passes to ``Strategy.on_event``.

Transcribed from the engine's concrete ``ante.engine._BacktestContext`` (which is NOT moved
here — only its public contract is). Keep this Protocol in lockstep with that class: it exists so
a strategy bundle running in its own venv (against this SDK alone, with no ``ante`` engine
installed) can type its ``on_event(event, ctx)`` against the real shape of ``ctx`` without
importing the engine.
"""

from __future__ import annotations

import random
from collections.abc import Sequence
from typing import Protocol, runtime_checkable

from ante_strategy_sdk.datafeeds import DataFeedProvider
from ante_strategy_sdk.types import Market, Order, Position


@runtime_checkable
class AgentContext(Protocol):
    """Read/write surface a strategy's ``on_event(event, ctx)`` sees each step.

    ``rng`` is a seeded ``random.Random`` — strategies MUST use it (not the stdlib ``random``
    module directly) for any randomness, so a backtest stays a pure function of
    (snapshot, strategy, seed, code version).

    ``data`` is the point-in-time data-feed provider seam (plan A1.4) — see
    ``ante_strategy_sdk.datafeeds``. A backtest wires the null/recorded provider; strategies that
    don't declare feeds never touch it.

    ``runtime_checkable``: ``isinstance(obj, AgentContext)`` checks that the four methods AND the
    ``rng``/``data`` attributes exist on ``obj`` (attribute presence only — never signatures or
    types).
    """

    rng: random.Random
    data: DataFeedProvider

    def get_markets(self) -> Sequence[Market]:
        """The full field for this run (one market for a single-leg backtest)."""
        ...

    def get_positions(self) -> Sequence[Position]:
        """A fresh snapshot of open positions. Mutating the result does not affect the engine."""
        ...

    def place_order(self, order: Order) -> None:
        """Queue an order. The engine stamps ``placed_at_ts`` at the current event's timestamp;
        any ``placed_at_ts`` set by the caller is overwritten.
        """
        ...

    def cancel_order(self, order_id: str) -> None:
        """Cancel a previously placed, still-open order by id (no-op if already filled/gone)."""
        ...
