"""Order management module for OpenClaw exchange operations.

This module provides OrderManager for managing trading orders with support
for both paper trading simulation and live exchange trading.
"""

from __future__ import annotations

import asyncio
import uuid
from datetime import datetime
from enum import Enum
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field

from openclaw.exchange.models import Order, OrderSide, OrderStatus, OrderType, Position
from openclaw.trading import OrderManager as TradingOrderManager
from openclaw.trading import OrderRequest, OrderFilters
from openclaw.trading.live_mode import LiveModeManager, TradingMode
from openclaw.utils.logging import get_logger


class TradingModeEnum(str, Enum):
    """Trading mode for the system."""

    PAPER = "paper"
    LIVE = "live"


class CreateOrderRequest(BaseModel):
    """Request to create a new order.

    Attributes:
        symbol: Trading pair/symbol (e.g., "BTC/USDT")
        side: Buy or sell
        order_type: Market, limit, stop_loss, etc.
        amount: Order quantity
        price: Order price (required for limit orders)
    """

    symbol: str = Field(..., description="Trading symbol")
    side: OrderSide = Field(..., description="Buy or sell")
    order_type: OrderType = Field(default=OrderType.MARKET, description="Order type")
    amount: float = Field(..., gt=0, description="Order quantity")
    price: Optional[float] = Field(default=None, ge=0, description="Limit price")


class ClosePositionRequest(BaseModel):
    """Request to close a position.

    Attributes:
        symbol: Trading symbol to close position for
        amount: Amount to close (optional, closes all if not specified)
        order_type: Market or limit order
        price: Limit price (if limit order)
    """

    symbol: str = Field(..., description="Trading symbol")
    amount: Optional[float] = Field(default=None, gt=0, description="Amount to close")
    order_type: OrderType = Field(default=OrderType.MARKET, description="Order type")
    price: Optional[float] = Field(default=None, ge=0, description="Limit price")


class OrderSummary(BaseModel):
    """Summary of order statistics.

    Attributes:
        total_orders: Total number of orders
        open_orders: Number of open/pending orders
        filled_orders: Number of filled orders
        cancelled_orders: Number of cancelled orders
        total_volume: Total trading volume
        total_fees: Total fees paid
    """

    total_orders: int = 0
    open_orders: int = 0
    filled_orders: int = 0
    cancelled_orders: int = 0
    total_volume: float = 0.0
    total_fees: float = 0.0


class PositionSummary(BaseModel):
    """Summary of position information.

    Attributes:
        symbol: Trading symbol
        side: Position side (buy=long, sell=short)
        amount: Position size
        entry_price: Average entry price
        current_price: Current market price
        unrealized_pnl: Unrealized profit/loss
        unrealized_pnl_pct: Unrealized PnL percentage
        market_value: Current market value
    """

    symbol: str
    side: OrderSide
    amount: float
    entry_price: float
    current_price: Optional[float] = None
    unrealized_pnl: float = 0.0
    unrealized_pnl_pct: float = 0.0
    market_value: float = 0.0


class TradingModeStatus(BaseModel):
    """Current trading mode status.

    Attributes:
        mode: Current trading mode (paper/live)
        is_live: Whether in live trading mode
        daily_limit_usd: Daily trading limit
        daily_traded_usd: Amount traded today
        daily_remaining_usd: Remaining daily limit
        trade_count_today: Number of trades today
    """

    mode: str
    is_live: bool
    daily_limit_usd: float
    daily_traded_usd: float
    daily_remaining_usd: float
    trade_count_today: int
    confirmation_required: bool


class ExchangeOrderManager:
    """Manager for exchange trading operations.

    Provides a unified interface for order management with support for
    both paper trading simulation and live exchange trading.

    Args:
        paper_trading: Whether to use paper trading simulation
        fee_rate: Trading fee rate (default 0.001 = 0.1%)
    """

    def __init__(
        self,
        paper_trading: bool = True,
        fee_rate: float = 0.001,
    ):
        self.paper_trading = paper_trading
        self.fee_rate = fee_rate
        self.logger = get_logger("exchange.order_manager")

        # Initialize live mode manager
        self._live_mode_manager = LiveModeManager()
        if not paper_trading:
            self._live_mode_manager.enable_live_mode()

        # Initialize underlying order manager
        self._order_manager = TradingOrderManager(
            live_mode_manager=self._live_mode_manager,
            fee_rate=fee_rate,
            paper_trading=paper_trading,
        )

        self.logger.info(
            f"ExchangeOrderManager initialized: mode={self.trading_mode.value}"
        )

    @property
    def trading_mode(self) -> TradingModeEnum:
        """Get current trading mode."""
        if self._live_mode_manager.is_live_mode:
            return TradingModeEnum.LIVE
        return TradingModeEnum.PAPER

    @property
    def is_paper_trading(self) -> bool:
        """Check if in paper trading mode."""
        return self.trading_mode == TradingModeEnum.PAPER

    @property
    def is_live_trading(self) -> bool:
        """Check if in live trading mode."""
        return self.trading_mode == TradingModeEnum.LIVE

    async def start(self) -> None:
        """Start the order manager."""
        await self._order_manager.start()
        self.logger.info("ExchangeOrderManager started")

    async def stop(self) -> None:
        """Stop the order manager."""
        await self._order_manager.stop()
        self.logger.info("ExchangeOrderManager stopped")

    async def create_order(
        self,
        request: CreateOrderRequest,
        agent_id: Optional[str] = None,
    ) -> Order:
        """Create a new order.

        Args:
            request: Order creation request
            agent_id: Optional agent ID for the order

        Returns:
            Created order
        """
        order_request = OrderRequest(
            symbol=request.symbol,
            side=request.side,
            order_type=request.order_type,
            amount=request.amount,
            price=request.price,
        )

        order = await self._order_manager.create_order(order_request, agent_id)
        self.logger.info(f"Order created via exchange API: {order.order_id}")
        return order

    async def cancel_order(self, order_id: str) -> Optional[Order]:
        """Cancel an open order.

        Args:
            order_id: Order ID to cancel

        Returns:
            Cancelled order or None if not found
        """
        order = await self._order_manager.cancel_order(order_id)
        if order:
            self.logger.info(f"Order cancelled via exchange API: {order_id}")
        return order

    async def get_order(self, order_id: str) -> Optional[Order]:
        """Get order by ID.

        Args:
            order_id: Order ID

        Returns:
            Order or None if not found
        """
        return await self._order_manager.get_order(order_id)

    async def list_orders(
        self,
        symbol: Optional[str] = None,
        side: Optional[OrderSide] = None,
        status: Optional[OrderStatus] = None,
        order_type: Optional[OrderType] = None,
        limit: int = 100,
        offset: int = 0,
    ) -> List[Order]:
        """Get list of orders with optional filtering.

        Args:
            symbol: Filter by symbol
            side: Filter by side
            status: Filter by status
            order_type: Filter by order type
            limit: Maximum number of orders
            offset: Offset for pagination

        Returns:
            List of orders
        """
        filters = OrderFilters(
            symbol=symbol,
            side=side,
            status=status,
            order_type=order_type,
        )
        return await self._order_manager.get_orders(filters, limit, offset)

    async def get_position(self, symbol: str) -> Optional[Position]:
        """Get position for a symbol.

        Args:
            symbol: Trading symbol

        Returns:
            Position or None if not found
        """
        return await self._order_manager.get_position(symbol)

    async def list_positions(self) -> List[Position]:
        """Get all positions.

        Returns:
            List of positions
        """
        return await self._order_manager.get_all_positions()

    async def close_position(
        self,
        request: ClosePositionRequest,
        agent_id: Optional[str] = None,
    ) -> Optional[Order]:
        """Close a position by creating an opposite order.

        Args:
            request: Close position request
            agent_id: Optional agent ID

        Returns:
            Created closing order or None if position not found
        """
        symbol = request.symbol.upper()
        position = await self.get_position(symbol)

        if not position:
            self.logger.warning(f"No position found for {symbol}")
            return None

        # Determine closing side (opposite of current position)
        close_side = OrderSide.SELL if position.side == OrderSide.BUY else OrderSide.BUY

        # Determine amount to close
        close_amount = request.amount or position.amount
        close_amount = min(close_amount, position.amount)

        # Create closing order
        order_request = CreateOrderRequest(
            symbol=symbol,
            side=close_side,
            order_type=request.order_type,
            amount=close_amount,
            price=request.price,
        )

        order = await self.create_order(order_request, agent_id)
        self.logger.info(
            f"Position close order created: {order.order_id} for {symbol} "
            f"({position.side} -> {close_side}, amount={close_amount})"
        )
        return order

    async def get_order_summary(self) -> OrderSummary:
        """Get summary of orders.

        Returns:
            Order summary statistics
        """
        summary = await self._order_manager.get_account_summary()
        return OrderSummary(
            total_orders=summary.get("total_orders", 0),
            open_orders=summary.get("open_orders", 0),
            filled_orders=summary.get("filled_orders", 0),
            cancelled_orders=summary.get("cancelled_orders", 0),
            total_volume=summary.get("total_volume", 0.0),
            total_fees=summary.get("total_fees", 0.0),
        )

    async def get_position_summaries(self) -> List[PositionSummary]:
        """Get summaries of all positions with calculated metrics.

        Returns:
            List of position summaries
        """
        # Update position prices first
        await self._order_manager.update_position_prices()

        positions = await self.list_positions()
        summaries = []

        for pos in positions:
            summaries.append(
                PositionSummary(
                    symbol=pos.symbol,
                    side=pos.side,
                    amount=pos.amount,
                    entry_price=pos.entry_price,
                    current_price=pos.current_price,
                    unrealized_pnl=pos.unrealized_pnl,
                    unrealized_pnl_pct=pos.unrealized_pnl_pct,
                    market_value=pos.market_value,
                )
            )

        return summaries

    def get_trading_mode_status(self) -> TradingModeStatus:
        """Get current trading mode status.

        Returns:
            Trading mode status
        """
        stats = self._live_mode_manager.get_live_stats()
        return TradingModeStatus(
            mode=stats["mode"],
            is_live=stats["is_live"],
            daily_limit_usd=stats["daily_limit_usd"],
            daily_traded_usd=stats["daily_traded_usd"],
            daily_remaining_usd=stats["daily_remaining_usd"],
            trade_count_today=stats["trade_count_today"],
            confirmation_required=stats["confirmation_required"],
        )

    def set_trading_mode(self, mode: TradingModeEnum) -> bool:
        """Set trading mode.

        Args:
            mode: Target trading mode

        Returns:
            True if mode was set successfully
        """
        if mode == TradingModeEnum.LIVE:
            success = self._live_mode_manager.enable_live_mode()
        else:
            success = self._live_mode_manager.disable_live_mode()

        if success:
            self.paper_trading = (mode == TradingModeEnum.PAPER)
            self._order_manager.paper_trading = self.paper_trading
            self.logger.warning(f"Trading mode changed to: {mode.value}")

        return success

    def toggle_trading_mode(self) -> TradingModeStatus:
        """Toggle between paper and live trading modes.

        Returns:
            Updated trading mode status
        """
        new_mode = (
            TradingModeEnum.PAPER
            if self.trading_mode == TradingModeEnum.LIVE
            else TradingModeEnum.LIVE
        )
        self.set_trading_mode(new_mode)
        return self.get_trading_mode_status()

    async def get_account_summary(self) -> Dict[str, Any]:
        """Get complete account summary.

        Returns:
            Account summary with orders, positions, and statistics
        """
        order_summary = await self.get_order_summary()
        position_summaries = await self.get_position_summaries()
        mode_status = self.get_trading_mode_status()

        total_position_value = sum(p.market_value for p in position_summaries)
        total_unrealized_pnl = sum(p.unrealized_pnl for p in position_summaries)

        return {
            "orders": order_summary.model_dump(),
            "positions": [p.model_dump() for p in position_summaries],
            "trading_mode": mode_status.model_dump(),
            "total_position_value": total_position_value,
            "total_unrealized_pnl": total_unrealized_pnl,
            "positions_count": len(position_summaries),
        }


# Singleton instance for application-wide use
_order_manager_instance: Optional[ExchangeOrderManager] = None


def get_order_manager() -> ExchangeOrderManager:
    """Get or create the singleton order manager instance."""
    global _order_manager_instance
    if _order_manager_instance is None:
        _order_manager_instance = ExchangeOrderManager(paper_trading=True)
    return _order_manager_instance


def set_order_manager(manager: ExchangeOrderManager) -> None:
    """Set the singleton order manager instance (for testing)."""
    global _order_manager_instance
    _order_manager_instance = manager
