"""Garleanu-Pedersen Optimal Execution pipeline functions.

Pipeline functions:
    gp_executor - Optimal execution with decaying alpha signal
"""

from __future__ import annotations

from typing import Any, Callable

from horizon._horizon import gp_optimal_trajectory, gp_urgency
from horizon.context import Context


def gp_executor(
    feed: str,
    target_position: float = 0.0,
    alpha_decay: float = 0.5,
    temporary_impact: float = 0.01,
    permanent_impact: float = 0.001,
    risk_aversion: float = 1.0,
    n_steps: int = 20,
    dt: float = 0.05,
) -> Callable[[Context], dict[str, Any]]:
    """Create a Garleanu-Pedersen optimal execution pipeline function.

    Computes optimal trade trajectory with exponentially decaying
    alpha signal and linear market impact.

    Args:
        feed: Feed name for price data.
        target_position: Target position to reach.
        alpha_decay: Alpha signal decay rate.
        temporary_impact: Temporary impact coefficient.
        permanent_impact: Permanent impact coefficient.
        risk_aversion: Risk aversion parameter.
        n_steps: Number of trajectory steps.
        dt: Time step size.

    Returns:
        Pipeline function: (Context) -> dict with keys:
            urgency, optimal_trade, trajectory_cost, progress
    """
    from horizon._horizon import auth_require_ultra
    auth_require_ultra()

    positions: dict[str, float] = {}

    def _gp_executor(ctx: Context) -> dict[str, Any]:
        market_id = ctx.market.id if ctx.market else "__default__"
        fd = ctx.feeds.get(feed)

        if market_id not in positions:
            positions[market_id] = 0.0

        current_pos = positions[market_id]

        result = {
            "urgency": 0.0,
            "optimal_trade": 0.0,
            "trajectory_cost": 0.0,
            "progress": 0.0,
        }

        if fd is not None and fd.price > 0:
            vol = max(ctx.params.get("volatility", 0.02), 0.001)
            urgency = gp_urgency(alpha_decay, temporary_impact, risk_aversion, vol)
            result["urgency"] = urgency

            try:
                traj = gp_optimal_trajectory(
                    current_pos, target_position,
                    alpha_decay, temporary_impact, permanent_impact,
                    risk_aversion, n_steps, dt,
                )
                if len(traj.positions) >= 2:
                    result["optimal_trade"] = traj.positions[1] - traj.positions[0]
                result["trajectory_cost"] = traj.total_cost

                if abs(target_position - positions.get("__initial__", current_pos)) > 1e-10:
                    done = abs(current_pos - target_position)
                    total = abs(positions.get("__initial__", 0.0) - target_position)
                    result["progress"] = max(0.0, 1.0 - done / max(total, 1e-10))
            except (ValueError, RuntimeError):
                pass

        return result

    _gp_executor.__name__ = "gp_executor"
    return _gp_executor
