"""Execution client for order management."""

from __future__ import annotations

from decimal import Decimal
from typing import Any, Dict, List, Optional

from zenotc.clients.base import BaseClient
from zenotc.exceptions import OrderNotFoundError
from zenotc.models.order import (
    BatchOrderRequest,
    BatchOrderResponse,
    Order,
    OrderRequest,
    OrderSide,
    OrderStatus,
    OrderType,
)
from zenotc.models.base import PaginatedResponse


class ExecutionClient(BaseClient):
    """Client for order execution and management."""

    async def create_order(
        self,
        side: str,
        asset: str,
        quantity: float | Decimal,
        price: Optional[float | Decimal] = None,
        order_type: str = "limit",
        time_in_force: str = "GTC",
        client_order_id: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Order:
        """Create a new order."""
        request = OrderRequest(
            side=OrderSide(side),
            asset=asset.upper(),
            quantity=Decimal(str(quantity)),
            price=Decimal(str(price)) if price is not None else None,
            order_type=OrderType(order_type),
            time_in_force=time_in_force,
            client_order_id=client_order_id,
            metadata=metadata,
        )

        await self._rate_limiter.acquire()
        response = await self._http.post("/api/v1/orders", json_data=request)
        return Order.model_validate(response)

    async def create_batch(self, orders: List[Dict[str, Any]]) -> BatchOrderResponse:
        """Create multiple orders in a single request."""
        order_requests = [
            OrderRequest(
                side=OrderSide(o["side"]),
                asset=o["asset"].upper(),
                quantity=Decimal(str(o["quantity"])),
                price=Decimal(str(o["price"])) if o.get("price") else None,
                order_type=OrderType(o.get("order_type", "limit")),
                time_in_force=o.get("time_in_force", "GTC"),
                client_order_id=o.get("client_order_id"),
                metadata=o.get("metadata"),
            )
            for o in orders
        ]

        request = BatchOrderRequest(orders=order_requests)
        await self._rate_limiter.acquire()
        response = await self._http.post("/api/v1/orders/batch", json_data=request)
        return BatchOrderResponse.model_validate(response)

    async def cancel_order(self, order_id: str) -> Order:
        """Cancel an order."""
        await self._rate_limiter.acquire()
        try:
            response = await self._http.delete(f"/api/v1/orders/{order_id}")
            return Order.model_validate(response)
        except Exception as e:
            if hasattr(e, "status_code") and e.status_code == 404:
                raise OrderNotFoundError(order_id) from e
            raise

    async def cancel_all(
        self, asset: Optional[str] = None, side: Optional[str] = None
    ) -> List[Order]:
        """Cancel all open orders.

        Args:
            asset: Optional asset to filter orders by
            side: Optional side ('buy' or 'sell') to filter orders by

        Returns:
            List of cancelled orders
        """
        body: Dict[str, Any] = {}
        if asset:
            body["asset"] = asset.upper()
        if side:
            body["side"] = side

        await self._rate_limiter.acquire()
        response = await self._http.delete("/api/v1/orders/batch", json_data=body if body else None)
        # Response is a list of cancelled orders
        if isinstance(response, list):
            return [Order.model_validate(o) for o in response]
        return [Order.model_validate(o) for o in response.get("cancelled", response.get("data", []))]

    async def get_order(self, order_id: str) -> Order:
        """Get order by ID."""
        await self._rate_limiter.acquire()
        try:
            response = await self._http.get(f"/api/v1/orders/{order_id}")
            return Order.model_validate(response)
        except Exception as e:
            if hasattr(e, "status_code") and e.status_code == 404:
                raise OrderNotFoundError(order_id) from e
            raise

    async def get_status(self, order_id: str) -> Order:
        """Get order execution status."""
        return await self.get_order(order_id)

    async def get_orders(
        self,
        status: Optional[str | List[str]] = None,
        asset: Optional[str] = None,
        side: Optional[str] = None,
        page: int = 1,
        limit: int = 50,
    ) -> PaginatedResponse[Order]:
        """Get orders with filtering and pagination."""
        params: Dict[str, Any] = {"page": page, "limit": limit}

        if status:
            if isinstance(status, list):
                params["statuses"] = ",".join(status)
            else:
                params["statuses"] = status
        if asset:
            params["asset"] = asset.upper()
        if side:
            params["side"] = side

        await self._rate_limiter.acquire()
        response = await self._http.get("/api/v1/orders", params=params)

        orders = [Order.model_validate(o) for o in response.get("data", [])]
        return PaginatedResponse[Order](
            data=orders,
            total=response.get("total", len(orders)),
            page=response.get("page", page),
            limit=response.get("limit", limit),
            has_more=response.get("hasMore", False),
        )

    async def get_open_orders(
        self, asset: Optional[str] = None, side: Optional[str] = None
    ) -> List[Order]:
        """Get all open orders."""
        result = await self.get_orders(
            status=[
                OrderStatus.PENDING.value,
                OrderStatus.OPEN.value,
                OrderStatus.PARTIALLY_FILLED.value,
            ],
            asset=asset,
            side=side,
            limit=100,
        )
        return result.data

    async def get_order_by_client_id(self, client_order_id: str) -> Order:
        """Get order by client-provided order ID."""
        await self._rate_limiter.acquire()
        try:
            response = await self._http.get(
                "/api/v1/orders/by-client-id",
                params={"clientOrderId": client_order_id},
            )
            return Order.model_validate(response)
        except Exception as e:
            if hasattr(e, "status_code") and e.status_code == 404:
                raise OrderNotFoundError(client_order_id) from e
            raise

    async def amend_order(
        self,
        order_id: str,
        quantity: Optional[float | Decimal] = None,
        price: Optional[float | Decimal] = None,
    ) -> Order:
        """Amend an existing order."""
        data: Dict[str, Any] = {}
        if quantity is not None:
            data["quantity"] = str(Decimal(str(quantity)))
        if price is not None:
            data["price"] = str(Decimal(str(price)))

        if not data:
            raise ValueError("At least one of quantity or price must be provided")

        await self._rate_limiter.acquire()
        try:
            response = await self._http.patch(f"/api/v1/orders/{order_id}", json_data=data)
            return Order.model_validate(response)
        except Exception as e:
            if hasattr(e, "status_code") and e.status_code == 404:
                raise OrderNotFoundError(order_id) from e
            raise
