"""Live trading mode implementation for OpenClaw trading system.

This module provides LiveModeManager for managing live vs simulated trading,
with enhanced risk controls, audit logging, and confirmation mechanisms.
"""

import json
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Any, Dict, List, Optional, Protocol

from pydantic import BaseModel, Field, field_validator

from openclaw.utils.logging import get_logger


class TradingMode(str, Enum):
    """Trading mode enumeration."""

    SIMULATED = "simulated"
    LIVE = "live"


class LiveTradeLogEntry(BaseModel):
    """Audit log entry for live trades.

    Attributes:
        timestamp: When the trade occurred
        symbol: Trading symbol
        side: Buy or sell
        amount: Trade amount
        price: Execution price
        order_id: Exchange order ID
        confirmation_code: User confirmation code
        risk_checks_passed: Whether risk checks passed
        daily_limit_before: Daily limit before trade
        daily_limit_after: Daily limit after trade
    """

    timestamp: str = Field(..., description="ISO format timestamp")
    symbol: str = Field(..., description="Trading symbol")
    side: str = Field(..., description="Trade side")
    amount: float = Field(..., gt=0, description="Trade amount")
    price: float = Field(..., gt=0, description="Execution price")
    order_id: str = Field(..., description="Exchange order ID")
    confirmation_code: str = Field(..., description="User confirmation code")
    risk_checks_passed: bool = Field(..., description="Risk check status")
    daily_limit_before: float = Field(..., ge=0, description="Limit before trade")
    daily_limit_after: float = Field(..., ge=0, description="Limit after trade")
    metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional data")


class LiveModeConfig(BaseModel):
    """Configuration for live trading mode.

    Attributes:
        enabled: Whether live mode is enabled
        daily_trade_limit_usd: Maximum USD value per day
        max_position_pct: Maximum position size as percentage of balance
        require_confirmation: Whether to require manual confirmation
        confirmation_timeout_seconds: Timeout for confirmation prompt
        audit_log_path: Path to audit log file
        alert_webhook_url: Optional webhook URL for alerts
    """

    enabled: bool = Field(default=False, description="Live mode enabled")
    daily_trade_limit_usd: float = Field(
        default=10000.0, gt=0, description="Daily trade limit in USD"
    )
    max_position_pct: float = Field(
        default=0.2, gt=0, le=1.0, description="Max position size as decimal"
    )
    require_confirmation: bool = Field(
        default=True, description="Require manual confirmation"
    )
    confirmation_timeout_seconds: int = Field(
        default=30, ge=5, le=300, description="Confirmation timeout"
    )
    audit_log_path: str = Field(
        default="logs/live_trades.jsonl", description="Audit log file path"
    )
    alert_webhook_url: Optional[str] = Field(
        default=None, description="Alert webhook URL"
    )

    @field_validator("alert_webhook_url")
    @classmethod
    def validate_webhook(cls, v: Optional[str]) -> Optional[str]:
        """Validate webhook URL format."""
        if v and not (v.startswith("http://") or v.startswith("https://")):
            raise ValueError("Webhook URL must start with http:// or https://")
        return v


class ConfirmationProvider(Protocol):
    """Protocol for confirmation providers."""

    def request_confirmation(
        self,
        message: str,
        timeout_seconds: int,
    ) -> tuple[bool, str]:
        """Request user confirmation.

        Args:
            message: Confirmation message to display
            timeout_seconds: Timeout for response

        Returns:
            Tuple of (confirmed, confirmation_code)
        """
        ...


class LiveModeManager:
    """Manager for live trading mode.

    Handles live/simulated mode switching, enhanced risk controls,
    audit logging, and confirmation mechanisms for safe live trading.

    Args:
        config: Live mode configuration
        confirmation_provider: Optional custom confirmation provider
    """

    def __init__(
        self,
        config: Optional[LiveModeConfig] = None,
        confirmation_provider: Optional[ConfirmationProvider] = None,
    ):
        self.config = config or LiveModeConfig()
        self.confirmation_provider = confirmation_provider
        self.logger = get_logger("trading.live_mode")

        # State tracking
        self._mode = TradingMode.LIVE if self.config.enabled else TradingMode.SIMULATED
        self._daily_traded_usd: float = 0.0
        self._last_trade_date: str = datetime.now().strftime("%Y-%m-%d")
        self._trade_count_today: int = 0
        self._audit_log: List[LiveTradeLogEntry] = []

        # Ensure log directory exists
        self._ensure_log_directory()

        self.logger.info(f"LiveModeManager initialized: mode={self._mode.value}")

    def _ensure_log_directory(self) -> None:
        """Create log directory if it doesn't exist."""
        log_path = Path(self.config.audit_log_path)
        log_path.parent.mkdir(parents=True, exist_ok=True)

    def _reset_daily_limits_if_needed(self) -> None:
        """Reset daily limits if it's a new day."""
        today = datetime.now().strftime("%Y-%m-%d")
        if today != self._last_trade_date:
            self._daily_traded_usd = 0.0
            self._trade_count_today = 0
            self._last_trade_date = today
            self.logger.info(f"Daily limits reset for new day: {today}")

    @property
    def is_live_mode(self) -> bool:
        """Check if currently in live trading mode."""
        return self._mode == TradingMode.LIVE and self.config.enabled

    @property
    def is_simulated_mode(self) -> bool:
        """Check if currently in simulated mode."""
        return self._mode == TradingMode.SIMULATED

    @property
    def mode_indicator(self) -> str:
        """Get visual mode indicator for UI display.

        Returns:
            String indicator with emoji for UI
        """
        if self.is_live_mode:
            return "🔴 LIVE MODE"
        return "🟢 SIMULATED MODE"

    def get_daily_limit_remaining(self) -> float:
        """Get remaining daily trade limit.

        Returns:
            USD amount remaining for today
        """
        self._reset_daily_limits_if_needed()
        return max(0.0, self.config.daily_trade_limit_usd - self._daily_traded_usd)

    def get_daily_limit(self) -> float:
        """Get total daily trade limit.

        Returns:
            USD daily limit amount
        """
        return self.config.daily_trade_limit_usd

    def validate_live_trade(
        self,
        symbol: str,
        amount: float,
        price: float,
        current_balance: float,
    ) -> tuple[bool, str]:
        """Validate a live trade with enhanced risk checks.

        Performs additional checks beyond normal validation when in live mode:
        - Daily trade limit
        - Position size limits
        - Sufficient balance with extra buffer

        Args:
            symbol: Trading symbol
            amount: Trade amount (base asset)
            price: Expected execution price
            current_balance: Current account balance

        Returns:
            Tuple of (is_valid, reason)
        """
        self._reset_daily_limits_if_needed()

        # Check if live mode is enabled
        if not self.is_live_mode:
            return False, "Not in live trading mode"

        trade_value = amount * price

        # Check daily limit
        remaining = self.get_daily_limit_remaining()
        if trade_value > remaining:
            return (
                False,
                f"Daily limit exceeded: ${trade_value:.2f} > ${remaining:.2f} remaining"
            )

        # Check position size limit (with extra buffer in live mode)
        position_value = trade_value
        max_position = current_balance * self.config.max_position_pct
        if position_value > max_position:
            return (
                False,
                f"Position size exceeds limit: ${position_value:.2f} > ${max_position:.2f} max"
            )

        # Extra balance check for live mode (1.5x buffer instead of normal 1.2x)
        required_with_buffer = trade_value * 1.5
        if current_balance < required_with_buffer:
            return (
                False,
                f"Insufficient balance with safety buffer: "
                f"${current_balance:.2f} < ${required_with_buffer:.2f} required"
            )

        return True, "Validation passed"

    def request_confirmation(
        self,
        symbol: str,
        side: str,
        amount: float,
        price: float,
    ) -> tuple[bool, str]:
        """Request user confirmation for a live trade.

        Args:
            symbol: Trading symbol
            side: Trade side (buy/sell)
            amount: Trade amount
            price: Expected price

        Returns:
            Tuple of (confirmed, confirmation_code)
        """
        if not self.config.require_confirmation:
            return True, "AUTO_CONFIRMED"

        trade_value = amount * price
        message = (
            f"🔴 LIVE TRADE CONFIRMATION REQUIRED\n"
            f"   Symbol: {symbol}\n"
            f"   Side: {side.upper()}\n"
            f"   Amount: {amount}\n"
            f"   Price: ${price:.2f}\n"
            f"   Total Value: ${trade_value:.2f}\n"
            f"   Daily Limit Remaining: ${self.get_daily_limit_remaining():.2f}\n"
            f"   Confirm within {self.config.confirmation_timeout_seconds} seconds"
        )

        if self.confirmation_provider:
            return self.confirmation_provider.request_confirmation(
                message, self.config.confirmation_timeout_seconds
            )

        # Default implementation: log and auto-confirm in test environments
        self.logger.warning(f"No confirmation provider configured, auto-confirming\n{message}")
        return True, f"AUTO_{datetime.now().strftime('%H%M%S')}"

    def log_live_trade(
        self,
        symbol: str,
        side: str,
        amount: float,
        price: float,
        order_id: str,
        confirmation_code: str,
        risk_checks_passed: bool,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> None:
        """Log a live trade to audit log.

        Args:
            symbol: Trading symbol
            side: Trade side
            amount: Trade amount
            price: Execution price
            order_id: Exchange order ID
            confirmation_code: Confirmation code used
            risk_checks_passed: Whether risk checks passed
            metadata: Additional metadata
        """
        self._reset_daily_limits_if_needed()

        limit_before = self.get_daily_limit_remaining()
        trade_value = amount * price

        # Update daily tracking
        self._daily_traded_usd += trade_value
        self._trade_count_today += 1

        limit_after = self.get_daily_limit_remaining()

        entry = LiveTradeLogEntry(
            timestamp=datetime.now().isoformat(),
            symbol=symbol,
            side=side,
            amount=amount,
            price=price,
            order_id=order_id,
            confirmation_code=confirmation_code,
            risk_checks_passed=risk_checks_passed,
            daily_limit_before=limit_before,
            daily_limit_after=limit_after,
            metadata=metadata or {},
        )

        self._audit_log.append(entry)

        # Write to file
        self._write_audit_log(entry)

        self.logger.info(
            f"Live trade logged: {side} {amount} {symbol} @ ${price:.2f} "
            f"(daily: ${self._daily_traded_usd:.2f})"
        )

    def _write_audit_log(self, entry: LiveTradeLogEntry) -> None:
        """Write audit log entry to file."""
        try:
            with open(self.config.audit_log_path, "a", encoding="utf-8") as f:
                f.write(entry.model_dump_json() + "\n")
        except Exception as e:
            self.logger.error(f"Failed to write audit log: {e}")

    def get_audit_log(
        self,
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
    ) -> List[LiveTradeLogEntry]:
        """Get audit log entries.

        Args:
            start_date: Optional start date filter (YYYY-MM-DD)
            end_date: Optional end date filter (YYYY-MM-DD)

        Returns:
            List of log entries
        """
        entries = self._audit_log

        if start_date:
            entries = [
                e for e in entries
                if e.timestamp >= start_date
            ]

        if end_date:
            entries = [
                e for e in entries
                if e.timestamp <= f"{end_date}T23:59:59"
            ]

        return entries

    def get_live_stats(self) -> Dict[str, Any]:
        """Get live trading statistics.

        Returns:
            Dictionary with trading statistics
        """
        self._reset_daily_limits_if_needed()

        return {
            "mode": self._mode.value,
            "is_live": self.is_live_mode,
            "daily_limit_usd": self.config.daily_trade_limit_usd,
            "daily_traded_usd": self._daily_traded_usd,
            "daily_remaining_usd": self.get_daily_limit_remaining(),
            "trade_count_today": self._trade_count_today,
            "max_position_pct": self.config.max_position_pct,
            "confirmation_required": self.config.require_confirmation,
        }

    def switch_mode(self, mode: TradingMode) -> bool:
        """Switch between live and simulated mode.

        Args:
            mode: Target mode

        Returns:
            True if switch successful
        """
        if mode == TradingMode.LIVE and not self.config.enabled:
            self.logger.error("Cannot switch to live mode: not enabled in config")
            return False

        old_mode = self._mode
        self._mode = mode

        self.logger.warning(
            f"Trading mode switched: {old_mode.value} -> {mode.value}"
        )

        return True

    def enable_live_mode(self) -> bool:
        """Enable live trading mode.

        Returns:
            True if enabled successfully
        """
        self.config.enabled = True
        return self.switch_mode(TradingMode.LIVE)

    def disable_live_mode(self) -> bool:
        """Disable live trading mode (switch to simulated).

        Returns:
            True if disabled successfully
        """
        return self.switch_mode(TradingMode.SIMULATED)

    def __repr__(self) -> str:
        """String representation."""
        return (
            f"LiveModeManager("
            f"mode={self._mode.value}, "
            f"daily={self._daily_traded_usd:.2f}/{self.config.daily_trade_limit_usd:.2f}"
            f")"
        )
