"""Market making strategy client for automated quoting strategies."""

from __future__ import annotations

from typing import Any, Dict, List, Optional

from zenotc.clients.base import BaseClient
from zenotc.models.market_making_strategy import (
    MarketMaker,
    MarketMakerEvent,
    MarketMakerStatus,
    QuotingStrategy,
    InventoryMode,
)
from zenotc.models.base import PaginatedResponse


class MarketMakingStrategyClient(BaseClient):
    """Client for market making strategy management.

    Supports creating, configuring, and controlling automated market makers.
    """

    # ============ Core Methods ============

    async def create(
        self,
        asset: str,
        quoting_strategy: str,
        inventory_mode: str,
        spread_config: Dict[str, Any],
        quote_size_config: Dict[str, Any],
        inventory_config: Dict[str, Any],
        risk_config: Dict[str, Any],
        pricing_config: Dict[str, Any],
        refresh_config: Dict[str, Any],
        name: Optional[str] = None,
        description: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> MarketMaker:
        """Create a new market maker configuration.

        The market maker is created in 'stopped' status and must be started separately.

        Args:
            asset: Asset symbol
            quoting_strategy: 'fixed_spread', 'dynamic_spread', 'inventory_skew', or 'market_adaptive'
            inventory_mode: 'passive', 'active_rebalance', 'hedge', or 'limit_and_alert'
            spread_config: Spread configuration
            quote_size_config: Quote size configuration
            inventory_config: Inventory management configuration
            risk_config: Risk limits configuration
            pricing_config: Pricing source configuration
            refresh_config: Quote refresh configuration
            name: Optional name for the strategy
            description: Optional description
            metadata: Optional metadata

        Returns:
            The created market maker
        """
        data: Dict[str, Any] = {
            "asset": asset.upper(),
            "quotingStrategy": quoting_strategy,
            "inventoryMode": inventory_mode,
            "spreadConfig": spread_config,
            "quoteSizeConfig": quote_size_config,
            "inventoryConfig": inventory_config,
            "riskConfig": risk_config,
            "pricingConfig": pricing_config,
            "refreshConfig": refresh_config,
        }

        if name:
            data["name"] = name
        if description:
            data["description"] = description
        if metadata:
            data["metadata"] = metadata

        await self._rate_limiter.acquire()
        response = await self._http.post("/api/sdk/v1/market-making/configs", json_data=data)
        return MarketMaker.model_validate(response)

    async def get(self, config_id: str) -> MarketMaker:
        """Get a market maker by config ID.

        Args:
            config_id: The market maker config ID

        Returns:
            The market maker
        """
        await self._rate_limiter.acquire()
        response = await self._http.get(f"/api/sdk/v1/market-making/configs/{config_id}")
        return MarketMaker.model_validate(response)

    async def list(
        self,
        status: Optional[str] = None,
        asset: Optional[str] = None,
        page: int = 1,
        limit: int = 50,
    ) -> PaginatedResponse[MarketMaker]:
        """List market makers with optional filtering.

        Args:
            status: Filter by status
            asset: Filter by asset
            page: Page number
            limit: Results per page

        Returns:
            Paginated list of market makers
        """
        params: Dict[str, Any] = {"page": page, "limit": limit}

        if status:
            params["status"] = status
        if asset:
            params["asset"] = asset.upper()

        await self._rate_limiter.acquire()
        response = await self._http.get("/api/sdk/v1/market-making/configs", params=params)

        market_makers = [MarketMaker.model_validate(mm) for mm in response.get("data", [])]
        return PaginatedResponse[MarketMaker](
            data=market_makers,
            total=response.get("total", len(market_makers)),
            page=response.get("page", page),
            limit=response.get("limit", limit),
            has_more=response.get("hasMore", False),
        )

    async def update(
        self,
        config_id: str,
        name: Optional[str] = None,
        description: Optional[str] = None,
        spread_config: Optional[Dict[str, Any]] = None,
        quote_size_config: Optional[Dict[str, Any]] = None,
        inventory_config: Optional[Dict[str, Any]] = None,
        risk_config: Optional[Dict[str, Any]] = None,
        pricing_config: Optional[Dict[str, Any]] = None,
        refresh_config: Optional[Dict[str, Any]] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> MarketMaker:
        """Update a market maker configuration.

        The market maker must be stopped or paused to update configuration.

        Args:
            config_id: The market maker config ID
            name: Updated name
            description: Updated description
            spread_config: Updated spread config
            quote_size_config: Updated quote size config
            inventory_config: Updated inventory config
            risk_config: Updated risk config
            pricing_config: Updated pricing config
            refresh_config: Updated refresh config
            metadata: Updated metadata

        Returns:
            The updated market maker
        """
        data: Dict[str, Any] = {}

        if name is not None:
            data["name"] = name
        if description is not None:
            data["description"] = description
        if spread_config is not None:
            data["spreadConfig"] = spread_config
        if quote_size_config is not None:
            data["quoteSizeConfig"] = quote_size_config
        if inventory_config is not None:
            data["inventoryConfig"] = inventory_config
        if risk_config is not None:
            data["riskConfig"] = risk_config
        if pricing_config is not None:
            data["pricingConfig"] = pricing_config
        if refresh_config is not None:
            data["refreshConfig"] = refresh_config
        if metadata is not None:
            data["metadata"] = metadata

        await self._rate_limiter.acquire()
        response = await self._http.put(f"/api/sdk/v1/market-making/configs/{config_id}", json_data=data)
        return MarketMaker.model_validate(response)

    async def delete(self, config_id: str) -> None:
        """Delete a market maker configuration.

        The market maker must be stopped to delete.

        Args:
            config_id: The market maker config ID
        """
        await self._rate_limiter.acquire()
        await self._http.delete(f"/api/sdk/v1/market-making/configs/{config_id}")

    async def start(self, config_id: str) -> MarketMaker:
        """Start a market maker.

        Begins actively quoting on the configured asset.

        Args:
            config_id: The market maker config ID

        Returns:
            The updated market maker
        """
        await self._rate_limiter.acquire()
        response = await self._http.post(f"/api/sdk/v1/market-making/configs/{config_id}/start")
        return MarketMaker.model_validate(response)

    async def stop(self, config_id: str) -> MarketMaker:
        """Stop a market maker.

        Cancels all open quotes and stops quoting.

        Args:
            config_id: The market maker config ID

        Returns:
            The updated market maker
        """
        await self._rate_limiter.acquire()
        response = await self._http.post(f"/api/sdk/v1/market-making/configs/{config_id}/stop")
        return MarketMaker.model_validate(response)

    async def pause(self, config_id: str) -> MarketMaker:
        """Pause a market maker.

        Temporarily stops quoting while preserving state.

        Args:
            config_id: The market maker config ID

        Returns:
            The updated market maker
        """
        await self._rate_limiter.acquire()
        response = await self._http.post(f"/api/sdk/v1/market-making/configs/{config_id}/pause")
        return MarketMaker.model_validate(response)

    async def resume(self, config_id: str) -> MarketMaker:
        """Resume a paused market maker.

        Args:
            config_id: The market maker config ID

        Returns:
            The updated market maker
        """
        await self._rate_limiter.acquire()
        response = await self._http.post(f"/api/sdk/v1/market-making/configs/{config_id}/resume")
        return MarketMaker.model_validate(response)

    async def get_events(
        self,
        config_id: str,
        limit: int = 100,
    ) -> List[MarketMakerEvent]:
        """Get events for a market maker.

        Events include quote placements, fills, inventory updates, and errors.

        Args:
            config_id: The market maker config ID
            limit: Maximum number of events to return

        Returns:
            List of events
        """
        params = {"limit": limit} if limit else {}

        await self._rate_limiter.acquire()
        response = await self._http.get(
            f"/api/sdk/v1/market-making/configs/{config_id}/events",
            params=params,
        )

        if isinstance(response, list):
            return [MarketMakerEvent.model_validate(e) for e in response]
        return [MarketMakerEvent.model_validate(e) for e in response.get("data", [])]

    # ============ Convenience Methods ============

    async def create_fixed_spread_mm(
        self,
        asset: str,
        base_spread_bps: float,
        quote_size: float,
        name: Optional[str] = None,
        max_long_position: Optional[float] = None,
        max_short_position: Optional[float] = None,
        max_daily_loss: Optional[float] = None,
        refresh_interval_ms: Optional[int] = None,
        auto_start: bool = True,
    ) -> MarketMaker:
        """Create and optionally start a simple fixed-spread market maker.

        Args:
            asset: Asset symbol
            base_spread_bps: Base spread in basis points
            quote_size: Base quote size
            name: Optional name
            max_long_position: Max long position (default: quote_size * 10)
            max_short_position: Max short position (default: -quote_size * 10)
            max_daily_loss: Max daily loss (default: 10000)
            refresh_interval_ms: Quote refresh interval (default: 1000)
            auto_start: Whether to start immediately (default: True)

        Returns:
            The market maker
        """
        if max_long_position is None:
            max_long_position = quote_size * 10
        if max_short_position is None:
            max_short_position = -(quote_size * 10)
        if max_daily_loss is None:
            max_daily_loss = 10000
        if refresh_interval_ms is None:
            refresh_interval_ms = 1000

        mm = await self.create(
            asset=asset,
            name=name or f"Fixed Spread MM - {asset}",
            quoting_strategy="fixed_spread",
            inventory_mode="limit_and_alert",
            spread_config={
                "baseSpreadBps": base_spread_bps,
                "minSpreadBps": int(base_spread_bps * 0.5),
                "maxSpreadBps": int(base_spread_bps * 3),
            },
            quote_size_config={
                "baseSize": quote_size,
                "minSize": quote_size * 0.1,
                "maxSize": quote_size * 5,
                "maxTotalExposure": quote_size * 100,
            },
            inventory_config={
                "targetPosition": 0,
                "maxLongPosition": max_long_position,
                "maxShortPosition": max_short_position,
                "rebalanceThreshold": quote_size * 5,
                "rebalanceTarget": 0,
                "softLimitPercent": 80,
            },
            risk_config={
                "maxDailyLoss": max_daily_loss,
                "maxDailyVolume": 1000000,
                "maxOrdersPerMinute": 60,
                "maxSpreadWidening": 3,
                "stopOnError": False,
                "maxConsecutiveErrors": 5,
            },
            pricing_config={
                "source": "internal",
                "staleThresholdMs": 5000,
                "maxDeviationPercent": 5,
            },
            refresh_config={
                "intervalMs": refresh_interval_ms,
                "onFillRefresh": True,
                "onPriceChangeRefresh": True,
                "priceChangeThresholdBps": int(base_spread_bps / 2),
            },
        )

        if auto_start:
            mm = await self.start(mm.config_id)

        return mm

    async def create_dynamic_spread_mm(
        self,
        asset: str,
        base_spread_bps: float,
        quote_size: float,
        name: Optional[str] = None,
        volatility_multiplier: float = 1.5,
        inventory_skew_factor: float = 10,
        max_long_position: Optional[float] = None,
        max_short_position: Optional[float] = None,
        max_daily_loss: Optional[float] = None,
        auto_start: bool = True,
    ) -> MarketMaker:
        """Create and optionally start a dynamic spread market maker with inventory skew.

        Args:
            asset: Asset symbol
            base_spread_bps: Base spread in basis points
            quote_size: Base quote size
            name: Optional name
            volatility_multiplier: Volatility adjustment multiplier (default: 1.5)
            inventory_skew_factor: Inventory skew factor (default: 10)
            max_long_position: Max long position
            max_short_position: Max short position
            max_daily_loss: Max daily loss
            auto_start: Whether to start immediately (default: True)

        Returns:
            The market maker
        """
        if max_long_position is None:
            max_long_position = quote_size * 10
        if max_short_position is None:
            max_short_position = -(quote_size * 10)
        if max_daily_loss is None:
            max_daily_loss = 10000

        mm = await self.create(
            asset=asset,
            name=name or f"Dynamic MM - {asset}",
            quoting_strategy="market_adaptive",
            inventory_mode="active_rebalance",
            spread_config={
                "baseSpreadBps": base_spread_bps,
                "minSpreadBps": int(base_spread_bps * 0.5),
                "maxSpreadBps": int(base_spread_bps * 5),
                "volatilityMultiplier": volatility_multiplier,
                "inventorySkewFactor": inventory_skew_factor,
                "timeDecayEnabled": True,
            },
            quote_size_config={
                "baseSize": quote_size,
                "minSize": quote_size * 0.1,
                "maxSize": quote_size * 5,
                "maxTotalExposure": quote_size * 100,
            },
            inventory_config={
                "targetPosition": 0,
                "maxLongPosition": max_long_position,
                "maxShortPosition": max_short_position,
                "rebalanceThreshold": quote_size * 3,
                "rebalanceTarget": 0,
                "softLimitPercent": 70,
            },
            risk_config={
                "maxDailyLoss": max_daily_loss,
                "maxDailyVolume": 1000000,
                "maxOrdersPerMinute": 60,
                "maxSpreadWidening": 5,
                "stopOnError": False,
                "maxConsecutiveErrors": 5,
            },
            pricing_config={
                "source": "internal",
                "staleThresholdMs": 3000,
                "maxDeviationPercent": 3,
            },
            refresh_config={
                "intervalMs": 500,
                "onFillRefresh": True,
                "onPriceChangeRefresh": True,
                "priceChangeThresholdBps": int(base_spread_bps / 4),
                "jitterMs": 100,
            },
        )

        if auto_start:
            mm = await self.start(mm.config_id)

        return mm

    async def get_active_market_makers(self) -> List[MarketMaker]:
        """Get all active market makers.

        Returns:
            List of active market makers
        """
        result = await self.list(status="active", limit=100)
        return result.data

    async def stop_all_active(self) -> List[MarketMaker]:
        """Stop all active market makers.

        Returns:
            List of stopped market makers
        """
        active = await self.get_active_market_makers()
        stopped = []
        for mm in active:
            try:
                stopped_mm = await self.stop(mm.config_id)
                stopped.append(stopped_mm)
            except Exception:
                pass  # Continue stopping others even if one fails
        return stopped

    async def get_performance_summary(
        self,
        config_id: str,
    ) -> Dict[str, Any]:
        """Get market maker performance summary.

        Args:
            config_id: The market maker config ID

        Returns:
            Performance summary dict
        """
        mm = await self.get(config_id)
        return {
            "totalPnL": mm.metrics.total_pnl,
            "dailyPnL": mm.state.daily_pnl,
            "totalVolume": mm.metrics.total_volume,
            "dailyVolume": mm.state.daily_volume,
            "fillRate": mm.metrics.fill_rate,
            "avgSpreadCapture": mm.metrics.avg_spread_capture,
            "currentPosition": mm.state.current_position,
        }
