"""Parity arbitrage: YES + NO on same exchange costs < $1.00.

When the combined ask prices for YES and NO on the same exchange
sum to less than $1.00, buying both sides locks in guaranteed profit.
"""

from __future__ import annotations

import logging
import time
from typing import Callable

from horizon._horizon import Quote
from horizon.context import Context

from ._types import ArbResult

logger = logging.getLogger(__name__)


def parity_arb_scanner(
    market_id: str,
    exchange: str = "paper",
    feed_name: str | None = None,
    min_edge: float = 0.005,
    max_size: float = 50.0,
    fee_rate: float = 0.002,
    auto_execute: bool = False,
    cooldown: float = 5.0,
) -> Callable[[Context], None]:
    """Create a pipeline function that scans for parity arbitrage.

    Args:
        market_id: Market to scan.
        exchange: Exchange name.
        feed_name: Feed to read prices from. Defaults to exchange name.
        min_edge: Minimum net edge to consider actionable.
        max_size: Maximum trade size.
        fee_rate: Fee rate per trade.
        auto_execute: If True, execute parity arb automatically.
        cooldown: Seconds between executions.

    Returns:
        Pipeline function: (Context) -> None
    """
    from horizon._horizon import auth_require_pro
    auth_require_pro()

    fname = feed_name or exchange
    last_exec_time = 0.0

    def _scanner(ctx: Context) -> None:
        nonlocal last_exec_time

        engine = ctx.params.get("engine")
        if engine is None:
            return None

        opp = engine.scan_parity_arb(market_id, fname, fee_rate, max_size)
        if opp is None or opp.net_edge < min_edge:
            return None

        ctx.params["last_parity_arb"] = opp

        if auto_execute:
            now = time.monotonic()
            if now - last_exec_time >= cooldown:
                try:
                    yes_id, no_id = engine.execute_parity_arb(
                        market_id,
                        exchange,
                        opp.yes_ask,
                        opp.no_ask,
                        min(opp.max_size, max_size),
                    )
                    last_exec_time = now
                    ctx.params["last_parity_arb_result"] = ArbResult(
                        market_id=market_id,
                        buy_exchange=exchange,
                        sell_exchange=exchange,
                        buy_price=opp.yes_ask,
                        sell_price=opp.no_ask,
                        size=min(opp.max_size, max_size),
                        raw_edge=opp.raw_edge,
                        net_edge=opp.net_edge,
                        buy_order_id=yes_id,
                        sell_order_id=no_id,
                    )
                    logger.info(
                        "Parity arb executed: %s edge=%.4f",
                        market_id, opp.net_edge,
                    )
                except Exception as e:
                    logger.warning("Parity arb execution failed for %s: %s", market_id, e)

        return None

    _scanner.__name__ = "parity_arb_scanner"
    return _scanner


def parity_arb_sweep(
    engine,
    market_id: str,
    feed_name: str | None = None,
    exchange: str = "paper",
    min_edge: float = 0.005,
    max_size: float = 50.0,
    fee_rate: float = 0.002,
) -> ArbResult | None:
    """One-shot parity arbitrage sweep.

    Args:
        engine: Horizon Engine instance.
        market_id: Market to scan.
        feed_name: Feed to read prices from.
        exchange: Exchange name.
        min_edge: Minimum net edge.
        max_size: Maximum trade size.
        fee_rate: Fee rate per trade.

    Returns:
        ArbResult if executed, None if no opportunity found.
    """
    from horizon._horizon import auth_require_pro
    auth_require_pro()

    fname = feed_name or exchange
    opp = engine.scan_parity_arb(market_id, fname, fee_rate, max_size)

    if opp is None or opp.net_edge < min_edge:
        return None

    trade_size = min(opp.max_size, max_size)

    try:
        yes_id, no_id = engine.execute_parity_arb(
            market_id, exchange, opp.yes_ask, opp.no_ask, trade_size,
        )
    except Exception as e:
        logger.warning("Parity arb sweep failed for %s: %s", market_id, e)
        return None

    return ArbResult(
        market_id=market_id,
        buy_exchange=exchange,
        sell_exchange=exchange,
        buy_price=opp.yes_ask,
        sell_price=opp.no_ask,
        size=trade_size,
        raw_edge=opp.raw_edge,
        net_edge=opp.net_edge,
        buy_order_id=yes_id,
        sell_order_id=no_id,
    )
