from __future__ import annotations

from dataclasses import dataclass
from decimal import Decimal
import os
from typing import Any

from src.execution.order_intent import OrderIntent
from src.hyperliquid.rounding import HyperliquidAssetMeta, round_hyperliquid_price, round_hyperliquid_size


@dataclass(frozen=True)
class PreparedOrderPayload:
    accepted: bool
    reasons: tuple[str, ...]
    payload: dict[str, Any]
    stop_loss_payload: dict[str, Any] | None = None
    take_profit_payload: dict[str, Any] | None = None
    rounded_size: Decimal | None = None
    rounded_price: Decimal | None = None
    estimated_notional: Decimal | None = None
    cloid: str | None = None
    strategy_id: str | None = None


def prepare_order_payload(intent: OrderIntent, meta: HyperliquidAssetMeta, *, min_notional_usd: Decimal, require_testnet: bool = False) -> PreparedOrderPayload:
    reasons: list[str] = []
    if require_testnet and os.getenv("CTB_HL_ENV", "").lower() != "testnet":
        reasons.append("testnet_env_required")
    if intent.size <= 0:
        reasons.append("size_non_positive")
    if not intent.reduce_only and intent.stop_loss is None:
        reasons.append("stop_loss_required")

    rounded_size = round_hyperliquid_size(intent.size, meta) if intent.size > 0 else Decimal("0")
    reference_price = intent.price or intent.trigger_price or intent.stop_loss or Decimal("0")
    rounded_price = round_hyperliquid_price(reference_price) if reference_price > 0 else None
    estimated_notional = (rounded_size * rounded_price) if rounded_price is not None else intent.estimated_notional_usd
    if estimated_notional < min_notional_usd:
        reasons.append("min_notional")
    if rounded_size <= 0:
        reasons.append("rounded_size_zero")

    payload = {
        "coin": intent.coin,
        "asset_id": meta.asset_id,
        "is_buy": intent.side == "buy",
        "side": intent.side,
        "reduce_only": intent.reduce_only,
        "order_type": intent.order_type,
        "tif": intent.tif,
        "size": format(rounded_size, f".{meta.sz_decimals}f"),
        "price": str(rounded_price) if rounded_price is not None else None,
        "trigger_price": str(round_hyperliquid_price(intent.trigger_price)) if intent.trigger_price else None,
        "cloid": intent.client_order_id,
    }
    stop_payload = None
    if intent.stop_loss is not None:
        stop_payload = {
            "coin": intent.coin,
            "is_buy": intent.side == "sell",
            "side": "sell" if intent.side == "buy" else "buy",
            "reduce_only": True,
            "order_type": "trigger",
            "trigger_price": str(round_hyperliquid_price(intent.stop_loss)),
            "size": payload["size"],
            "cloid": f"{intent.client_order_id}-sl",
        }
    tp_payload = None
    if intent.take_profit is not None:
        tp_payload = {
            "coin": intent.coin,
            "is_buy": intent.side == "sell",
            "side": "sell" if intent.side == "buy" else "buy",
            "reduce_only": True,
            "order_type": "trigger",
            "trigger_price": str(round_hyperliquid_price(intent.take_profit)),
            "size": payload["size"],
            "cloid": f"{intent.client_order_id}-tp",
        }
    return PreparedOrderPayload(
        accepted=not reasons,
        reasons=tuple(reasons),
        payload=payload,
        stop_loss_payload=stop_payload,
        take_profit_payload=tp_payload,
        rounded_size=rounded_size,
        rounded_price=rounded_price,
        estimated_notional=estimated_notional,
        cloid=intent.client_order_id,
        strategy_id=intent.strategy_id,
    )
