from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field


class OrderSide(str, Enum):
    BUY = "BUY"
    SELL = "SELL"


class OrderType(str, Enum):
    MARKET = "MARKET"
    LIMIT = "LIMIT"
    SL = "SL"
    SL_MARKET = "SL_MARKET"


class ProductType(str, Enum):
    INTRADAY = "INTRADAY"
    DELIVERY = "DELIVERY"
    CNC = "CNC"
    MIS = "MIS"


class ExecutionRequest(BaseModel):
    broker: str = Field(..., example="angel_one")
    symbol: str = Field(..., example="RELIANCE")
    exchange: str = Field(..., example="NSE")
    side: OrderSide
    quantity: int = Field(..., gt=0)
    order_type: OrderType
    product: ProductType
    price: Optional[float] = None
    trigger_price: Optional[float] = None
    # PHASE-2 (Goal 3): optional caller-supplied idempotency key. A repeat
    # request with the same key within the TTL window replays the cached
    # response instead of placing a second real order.
    idempotency_key: Optional[str] = None
    # TIER-1 (broker-native protection): when set, a broker-side STOPLOSS_MARKET
    # order is placed automatically after the entry fills, so the position is
    # protected server-side even if this process dies or the price feed stalls.
    # target_price is retained for reference/monitoring (software-managed).
    stop_loss: Optional[float] = None
    target_price: Optional[float] = None


class ExecutionResponse(BaseModel):
    success: bool
    message: str
    broker: str
    order_id: Optional[str] = None
    status: str
    symbol: str
    side: str
    quantity: int
    # PHASE-2.5 (Goal 2): `success`/`status` above only ever meant "broker
    # accepted the order" — never confirmation of an actual fill. Callers
    # that update local Position/Portfolio/Risk state MUST check
    # `fill_status`/`filled_quantity` instead of treating acceptance as a
    # fill. fill_status: FILLED | PARTIALLY_FILLED | REJECTED | CANCELLED |
    # PENDING (still open at the broker) | UNKNOWN (couldn't confirm either way).
    fill_status: str = "UNKNOWN"
    filled_quantity: int = 0
    avg_fill_price: float = 0.0
    # TIER-1: broker-side stop-loss order id (empty if none placed / failed).
    # sl_protected=False means the entry filled but the SL order did NOT get
    # placed — callers should treat the position as urgently unprotected.
    sl_order_id: str = ""
    sl_protected: bool = False


class TradeExecutionRequest(BaseModel):
    broker: str
    symbol: str
    exchange: str
    capital: float
    risk_percent: float
    entry_price: float
    stop_loss: float
    target_price: float
    ema: float
    rsi: float
    macd: float
    supertrend: float
    atr: float
    vwap: float
    strategy: str
    confidence: float