from __future__ import annotations

from decimal import Decimal
import os
from typing import Any

from src.execution.hyperliquid_live_executor import ExecutionResult, HyperliquidLiveExecutor
from src.execution.order_intent import OrderIntent
from src.execution.payloads import PreparedOrderPayload, prepare_order_payload
from src.hyperliquid.rounding import HyperliquidAssetMeta


class HyperliquidTestnetExecutor(HyperliquidLiveExecutor):
    """SDK-compatible executor boundary for testnet; separately wired from live."""

    def _check_testnet_gates(self) -> None:
        if os.getenv("CTB_HL_ENV", "").lower() != "testnet":
            raise PermissionError("CTB_HL_ENV must be testnet for testnet execution")
        if os.getenv("CTB_TESTNET_TRADING_ALLOWED", "").lower() != "true":
            raise PermissionError("CTB_TESTNET_TRADING_ALLOWED must be true before testnet execution")
        if os.getenv("CTB_TESTNET_CONFIRMATION") != self.expected_confirmation:
            raise PermissionError("CTB_TESTNET_CONFIRMATION does not match expected confirmation")

    def prepare_payload(self, intent: OrderIntent, meta: HyperliquidAssetMeta, *, min_notional_usd: Decimal) -> PreparedOrderPayload:
        return prepare_order_payload(intent, meta, min_notional_usd=min_notional_usd, require_testnet=True)

    def execute(self, intent: OrderIntent) -> ExecutionResult:
        if self.dry_run:
            return ExecutionResult(status="dry_run", intent=intent, side_effects=())
        self._check_testnet_gates()
        is_buy = intent.side == "buy"
        response = self.exchange.market_open(intent.coin, is_buy, float(intent.size), None, None)
        return ExecutionResult(status="testnet_submitted", intent=intent, exchange_response=response, side_effects=("hyperliquid_testnet_order",))

    def cancel_by_oid_or_cloid(self, coin: str, *, oid: int | None = None, cloid: str | None = None) -> Any:
        if self.dry_run:
            return {"status": "dry_run", "coin": coin, "oid": oid, "cloid": cloid}
        self._check_testnet_gates()
        if oid is not None:
            return self.exchange.cancel(coin, oid)
        if cloid is not None:
            return self.exchange.cancel_by_cloid(coin, cloid)
        raise ValueError("oid or cloid required")
