"""FastAPI dashboard application.

Real-time dashboard backend with WebSocket support for live updates.
"""

from __future__ import annotations

import asyncio
import json
import uuid
from contextlib import asynccontextmanager
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, AsyncGenerator, Dict, List, Optional

import yaml
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from loguru import logger
from starlette.requests import Request

from openclaw.core.config import ConfigLoader, OpenClawConfig
from openclaw.trading import OrderManager, OrderRequest, OrderFilters
from openclaw.exchange.models import OrderSide, OrderStatus, OrderType
from openclaw.exchange.order_manager import (
    ExchangeOrderManager,
    CreateOrderRequest,
    ClosePositionRequest,
    TradingModeEnum,
    get_order_manager as get_exchange_order_manager,
)

from openclaw.dashboard.models import (
    AgentDetail,
    AgentStatus,
    AlertLevel,
    AlertMessage,
    AlertType,
    CostBreakdown,
    DashboardState,
    EquityPoint,
    PnLDistribution,
    SystemMetrics,
    TradeRecord,
    WebSocketMessage,
)
from openclaw.utils.logging import get_logger
from openclaw.exchange.websocket_feed import (
    WebSocketManager,
    create_default_manager,
    Ticker,
    OrderBook,
    Trade,
    KlineData,
)

# Setup logging
dashboard_logger = get_logger("dashboard")

# Global state for demo purposes
# In production, this would be replaced with a proper database or state management
class DashboardStateManager:
    """Manages dashboard state including agents, trades, and alerts."""

    def __init__(self) -> None:
        """Initialize the state manager."""
        self.agents: Dict[str, AgentStatus] = {}
        self.trades: List[TradeRecord] = []
        self.alerts: List[AlertMessage] = []
        self.equity_history: List[EquityPoint] = []
        self.websocket_connections: List[WebSocket] = []
        self._lock = asyncio.Lock()
        self._running = False
        self._update_task: Optional[asyncio.Task] = None

        # Order manager
        self.order_manager = OrderManager(paper_trading=True)

        # Demo data initialization
        self._init_demo_data()

    def _init_demo_data(self) -> None:
        """Initialize with demo data."""
        # Add sample agents
        self.agents["bull_001"] = AgentStatus(
            agent_id="bull_001",
            balance=12500.0,
            initial_capital=10000.0,
            status="🚀 thriving",
            skill_level=0.75,
            win_rate=0.62,
            total_trades=45,
            winning_trades=28,
            losing_trades=17,
            unlocked_factors=5,
            is_bankrupt=False,
            is_active=True,
            current_activity="TRADE",
        )
        self.agents["bear_001"] = AgentStatus(
            agent_id="bear_001",
            balance=9200.0,
            initial_capital=10000.0,
            status="⚠️ struggling",
            skill_level=0.60,
            win_rate=0.48,
            total_trades=38,
            winning_trades=18,
            losing_trades=20,
            unlocked_factors=3,
            is_bankrupt=False,
            is_active=True,
            current_activity="ANALYZE",
        )
        self.agents["fundamental_001"] = AgentStatus(
            agent_id="fundamental_001",
            balance=5000.0,
            initial_capital=10000.0,
            status="🔴 critical",
            skill_level=0.45,
            win_rate=0.35,
            total_trades=25,
            winning_trades=9,
            losing_trades=16,
            unlocked_factors=2,
            is_bankrupt=False,
            is_active=True,
            current_activity="REST",
        )
        self.agents["risky_001"] = AgentStatus(
            agent_id="risky_001",
            balance=0.0,
            initial_capital=10000.0,
            status="💀 bankrupt",
            skill_level=0.30,
            win_rate=0.25,
            total_trades=15,
            winning_trades=4,
            losing_trades=11,
            unlocked_factors=1,
            is_bankrupt=True,
            is_active=False,
            current_activity=None,
        )

        # Add sample trades
        self.trades = [
            TradeRecord(
                trade_id=f"trade_{i:04d}",
                agent_id=agent_id,
                symbol=["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA"][i % 5],
                side=["buy", "sell"][i % 2],
                amount=100.0 + i * 10,
                price=150.0 + i * 5,
                value=(100.0 + i * 10) * (150.0 + i * 5),
                fee=5.0,
                pnl=([50.0, -30.0, 80.0, -20.0, 100.0][i % 5]) * (1 if i % 2 == 0 else -1),
                is_win=i % 2 == 0,
                timestamp=datetime.now() - timedelta(minutes=i * 5),
                strategy=["momentum", "mean_reversion", "breakout"][i % 3],
            )
            for i, agent_id in enumerate(["bull_001", "bear_001", "fundamental_001"] * 5)
        ]

        # Initialize equity history
        base_equity = 40000.0
        for i in range(30):
            self.equity_history.append(
                EquityPoint(
                    timestamp=datetime.now() - timedelta(days=29 - i),
                    equity=base_equity + (i * 100) + (i * i * 5),
                )
            )

    async def start(self) -> None:
        """Start the background update task."""
        self._running = True
        self._update_task = asyncio.create_task(self._background_updates())
        dashboard_logger.info("Dashboard state manager started")

    async def stop(self) -> None:
        """Stop the background update task."""
        self._running = False
        if self._update_task:
            self._update_task.cancel()
            try:
                await self._update_task
            except asyncio.CancelledError:
                pass
        dashboard_logger.info("Dashboard state manager stopped")

    async def _background_updates(self) -> None:
        """Background task for periodic updates and alert generation."""
        while self._running:
            try:
                await asyncio.sleep(5)  # Update every 5 seconds

                # Simulate agent updates
                await self._simulate_agent_updates()

                # Generate alerts based on conditions
                await self._check_alerts()

                # Broadcast updates to all connected clients
                await self._broadcast_update()

            except Exception as e:
                dashboard_logger.error(f"Error in background updates: {e}")

    async def _simulate_agent_updates(self) -> None:
        """Simulate agent activity for demo."""
        async with self._lock:
            import random

            for agent in self.agents.values():
                if not agent.is_bankrupt and random.random() < 0.3:
                    # Random balance fluctuation
                    change = random.uniform(-50, 100)
                    agent.balance = max(0, agent.balance + change)
                    agent.last_updated = datetime.now()

                    # Update status based on balance
                    ratio = agent.balance / agent.initial_capital
                    if ratio >= 1.5:
                        agent.status = "🚀 thriving"
                    elif ratio >= 1.1:
                        agent.status = "💪 stable"
                    elif ratio >= 0.8:
                        agent.status = "⚠️ struggling"
                    elif ratio >= 0.3:
                        agent.status = "🔴 critical"
                    else:
                        agent.status = "💀 bankrupt"
                        agent.is_bankrupt = True
                        agent.is_active = False

                    # Occasionally add a new trade
                    if random.random() < 0.2:
                        trade = TradeRecord(
                            trade_id=f"trade_{uuid.uuid4().hex[:8]}",
                            agent_id=agent.agent_id,
                            symbol=random.choice(["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA"]),
                            side=random.choice(["buy", "sell"]),
                            amount=random.uniform(50, 500),
                            price=random.uniform(100, 300),
                            value=0,
                            fee=random.uniform(2, 10),
                            pnl=random.uniform(-100, 200),
                            is_win=random.random() > 0.4,
                            timestamp=datetime.now(),
                            strategy=random.choice(["momentum", "mean_reversion", "breakout"]),
                        )
                        trade.value = trade.amount * trade.price
                        self.trades.insert(0, trade)
                        agent.total_trades += 1
                        if trade.is_win:
                            agent.winning_trades += 1
                        else:
                            agent.losing_trades += 1

            # Update equity history
            total_equity = sum(a.balance for a in self.agents.values())
            self.equity_history.append(
                EquityPoint(timestamp=datetime.now(), equity=total_equity)
            )
            # Keep last 100 points
            if len(self.equity_history) > 100:
                self.equity_history = self.equity_history[-100:]

            # Keep only recent trades
            if len(self.trades) > 100:
                self.trades = self.trades[:100]

    async def _check_alerts(self) -> None:
        """Check for alert conditions."""
        async with self._lock:
            for agent in self.agents.values():
                # Check for bankruptcy
                if agent.is_bankrupt and not any(
                    a.agent_id == agent.agent_id
                    and a.alert_type == AlertType.BANKRUPTCY
                    for a in self.alerts[-10:]
                ):
                    alert = AlertMessage(
                        alert_id=f"alert_{uuid.uuid4().hex[:8]}",
                        alert_type=AlertType.BANKRUPTCY,
                        level=AlertLevel.CRITICAL,
                        agent_id=agent.agent_id,
                        title="代理破产",
                        message=f"代理 {agent.agent_id} 已破产！",
                        details={"balance": agent.balance, "initial_capital": agent.initial_capital},
                    )
                    self.alerts.insert(0, alert)

                # Check for large loss
                pnl = agent.balance - agent.initial_capital
                if pnl < -3000 and not any(
                    a.agent_id == agent.agent_id
                    and a.alert_type == AlertType.LARGE_LOSS
                    for a in self.alerts[-10:]
                ):
                    alert = AlertMessage(
                        alert_id=f"alert_{uuid.uuid4().hex[:8]}",
                        alert_type=AlertType.LARGE_LOSS,
                        level=AlertLevel.ERROR,
                        agent_id=agent.agent_id,
                        title="检测到重大损失",
                        message=f"代理 {agent.agent_id} 遭受重大损失: ${pnl:.2f}",
                        details={"pnl": pnl, "pnl_pct": (pnl / agent.initial_capital) * 100},
                    )
                    self.alerts.insert(0, alert)

            # Keep only recent alerts
            if len(self.alerts) > 50:
                self.alerts = self.alerts[:50]

    async def _broadcast_update(self) -> None:
        """Broadcast state update to all connected WebSocket clients."""
        if not self.websocket_connections:
            return

        message = WebSocketMessage(
            type="agent_init",
            data={
                "agents": [a.model_dump(mode="json") for a in self.agents.values()],
                "metrics": self._calculate_metrics().model_dump(mode="json"),
                "recent_trades": [t.model_dump(mode="json") for t in self.trades[:10]],
                "alerts": [a.model_dump(mode="json") for a in self.alerts[:20]],
            },
        )

        disconnected = []
        for ws in self.websocket_connections:
            try:
                await ws.send_json(message.model_dump(mode="json"))
            except Exception:
                disconnected.append(ws)

        # Clean up disconnected clients
        for ws in disconnected:
            if ws in self.websocket_connections:
                self.websocket_connections.remove(ws)

    def _calculate_metrics(self) -> SystemMetrics:
        """Calculate system-wide metrics."""
        active = [a for a in self.agents.values() if a.is_active]
        bankrupt = [a for a in self.agents.values() if a.is_bankrupt]

        total_pnl = sum(a.balance - a.initial_capital for a in self.agents.values())
        total_trades = sum(a.total_trades for a in self.agents.values())
        total_volume = sum(t.value for t in self.trades)

        win_rates = [a.win_rate for a in self.agents.values() if a.total_trades > 0]
        avg_win_rate = sum(win_rates) / len(win_rates) if win_rates else 0.0

        return SystemMetrics(
            total_agents=len(self.agents),
            active_agents=len(active),
            bankrupt_agents=len(bankrupt),
            total_trades=total_trades,
            total_volume=total_volume,
            total_pnl=total_pnl,
            avg_win_rate=avg_win_rate,
            total_costs=CostBreakdown(
                token_costs=sum(a.balance * 0.001 for a in self.agents.values()),
                trade_fees=sum(t.fee for t in self.trades),
                data_costs=len(self.trades) * 0.01,
                total=0,
            ),
            system_equity=sum(a.balance for a in self.agents.values()),
        )

    async def connect_websocket(self, websocket: WebSocket) -> None:
        """Add a new WebSocket connection."""
        await websocket.accept()
        async with self._lock:
            self.websocket_connections.append(websocket)

        # Send initial state
        await self._send_initial_state(websocket)
        dashboard_logger.info(f"WebSocket client connected. Total: {len(self.websocket_connections)}")

    async def disconnect_websocket(self, websocket: WebSocket) -> None:
        """Remove a WebSocket connection."""
        async with self._lock:
            if websocket in self.websocket_connections:
                self.websocket_connections.remove(websocket)
        dashboard_logger.info(f"WebSocket client disconnected. Total: {len(self.websocket_connections)}")

    async def _send_initial_state(self, websocket: WebSocket) -> None:
        """Send initial dashboard state to new client."""
        metrics = self._calculate_metrics()

        # Calculate PnL distribution
        pnls = [t.pnl for t in self.trades if t.pnl is not None]
        distribution = self._calculate_pnl_distribution(pnls)

        state = DashboardState(
            agents=list(self.agents.values()),
            recent_trades=self.trades[:20],
            equity_curve=self.equity_history,
            pnl_distribution=distribution,
            metrics=metrics,
            alerts=self.alerts[:10],
        )

        message = WebSocketMessage(
            type="agent_init",
            data=state.model_dump(mode="json"),
        )
        await websocket.send_json(message.model_dump(mode="json"))

    def _calculate_pnl_distribution(self, pnls: List[float]) -> PnLDistribution:
        """Calculate PnL distribution for histogram."""
        bins = ["<-200", "-200 to -100", "-100 to 0", "0 to 100", "100 to 200", ">200"]
        counts = [0] * 6
        wins = [0] * 6
        losses = [0] * 6

        for pnl in pnls:
            if pnl < -200:
                idx = 0
            elif pnl < -100:
                idx = 1
            elif pnl < 0:
                idx = 2
            elif pnl < 100:
                idx = 3
            elif pnl < 200:
                idx = 4
            else:
                idx = 5

            counts[idx] += 1
            if pnl > 0:
                wins[idx] += 1
            else:
                losses[idx] += 1

        return PnLDistribution(bins=bins, counts=counts, wins=wins, losses=losses)

    def get_agent_detail(self, agent_id: str) -> Optional[AgentDetail]:
        """Get detailed information for a specific agent."""
        agent = self.agents.get(agent_id)
        if not agent:
            return None

        agent_trades = [t for t in self.trades if t.agent_id == agent_id]

        return AgentDetail(
            **agent.model_dump(mode="json"),
            trade_history=agent_trades,
            equity_curve=self.equity_history,  # Simplified
            cost_breakdown=CostBreakdown(
                token_costs=agent.balance * 0.001,
                trade_fees=sum(t.fee for t in agent_trades),
                data_costs=len(agent_trades) * 0.01,
                total=0,
            ),
            balance_history=[
                EquityPoint(
                    timestamp=datetime.now() - timedelta(days=i),
                    equity=agent.initial_capital + (agent.balance - agent.initial_capital) * (i / 30),
                )
                for i in range(30)
            ],
        )


# Global state manager instance
state_manager = DashboardStateManager()

# Global WebSocket manager for market data
ws_manager: Optional[WebSocketManager] = None


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator:
    """Application lifespan context manager."""
    global ws_manager
    # Startup
    await state_manager.start()

    # Initialize WebSocket manager with default feeds
    ws_manager = create_default_manager()
    await ws_manager.start()
    dashboard_logger.info("WebSocket manager started with market feeds")

    yield

    # Shutdown
    await state_manager.stop()
    if ws_manager:
        await ws_manager.stop()


def create_app() -> FastAPI:
    """Create and configure the FastAPI application."""
    app = FastAPI(
        title="OpenClaw Trading Dashboard",
        description="Real-time monitoring dashboard for OpenClaw trading system",
        version="0.1.0",
        lifespan=lifespan,
    )

    from fastapi.middleware.cors import CORSMiddleware
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],  # Allows all origins
        allow_credentials=False, # Must be False if allow_origins is ["*"]
        allow_methods=["*"],  # Allows all methods
        allow_headers=["*"],  # Allows all headers
        expose_headers=["*"],
    )

    @app.middleware("http")
    async def log_requests(request: Request, call_next):
        logger.debug(f"Incoming request: {request.method} {request.url}")
        response = await call_next(request)
        return response

    # Setup templates and static files
    templates_dir = Path(__file__).parent / "templates"
    templates = Jinja2Templates(directory=str(templates_dir))

    # Mount static files directory
    static_dir = Path(__file__).parent / "static"
    app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")

    @app.get("/", response_class=HTMLResponse)
    async def dashboard(request: Request) -> Any:
        """Serve the main dashboard page."""
        return templates.TemplateResponse("index.html", {"request": request})

    @app.get("/config", response_class=HTMLResponse)
    async def config_page(request: Request) -> Any:
        """Serve the configuration management page."""
        return templates.TemplateResponse("config.html", {"request": request})

    @app.get("/exchanges", response_class=HTMLResponse)
    async def exchanges_page(request: Request) -> Any:
        """Serve the exchange configuration management page."""
        return templates.TemplateResponse("exchanges.html", {"request": request})

    @app.get("/api/agents")
    async def get_agents() -> List[AgentStatus]:
        """Get all agent statuses."""
        return list(state_manager.agents.values())

    @app.get("/api/agents/{agent_id}")
    @app.get("/api/agent/{agent_id}")
    async def get_agent(agent_id: str) -> Optional[AgentDetail]:
        """Get detailed information for a specific agent."""
        return state_manager.get_agent_detail(agent_id)

    @app.get("/api/trades")
    async def get_trades(limit: int = 50) -> List[TradeRecord]:
        """Get recent trades."""
        return state_manager.trades[:limit]

    @app.get("/api/metrics")
    async def get_metrics() -> SystemMetrics:
        """Get system-wide metrics."""
        return state_manager._calculate_metrics()

    @app.get("/api/metrics/equity")
    async def get_equity_curve() -> List[EquityPoint]:
        """Get equity curve data for charts."""
        return state_manager.equity_history

    @app.get("/api/metrics/pnl-distribution")
    async def get_pnl_distribution() -> PnLDistribution:
        """Get profit/loss distribution for charts."""
        pnls = [t.pnl for t in state_manager.trades if t.pnl is not None]
        return state_manager._calculate_pnl_distribution(pnls)

    @app.get("/api/alerts")
    async def get_alerts(limit: int = 20) -> List[AlertMessage]:
        """Get recent alerts."""
        return state_manager.alerts[:limit]

    @app.post("/api/alerts/{alert_id}/acknowledge")
    async def acknowledge_alert(alert_id: str) -> Dict[str, str]:
        """Acknowledge an alert."""
        for alert in state_manager.alerts:
            if alert.alert_id == alert_id:
                alert.acknowledged = True
                return {"status": "已确认"}
        return {"status": "未找到"}

    @app.get("/api/config")
    async def get_config() -> Dict[str, Any]:
        """Get current configuration from config/default.yaml."""
        try:
            config = ConfigLoader.load("config/default.yaml")
            return config.model_dump(mode="json")
        except Exception as e:
            dashboard_logger.error(f"Error loading config: {e}")
            return {"error": f"加载配置失败: {str(e)}"}

    @app.post("/api/config")
    async def save_config(config_data: Dict[str, Any]) -> Dict[str, str]:
        """Save configuration with validation."""
        try:
            # Validate using Pydantic model
            config = OpenClawConfig(**config_data)

            # Save to config/default.yaml
            output_path = Path("config/default.yaml")
            output_path.parent.mkdir(parents=True, exist_ok=True)

            # Convert to YAML
            yaml_content = ConfigLoader._config_to_yaml(config)
            output_path.write_text(yaml_content, encoding="utf-8")

            dashboard_logger.info("Configuration saved successfully")
            return {"status": "success", "message": "配置已保存"}
        except Exception as e:
            dashboard_logger.error(f"Error saving config: {e}")
            return {"status": "error", "message": str(e)}

    @app.get("/api/config/schema")
    async def get_config_schema() -> Dict[str, Any]:
        """Return JSON schema for all config fields with types and descriptions."""
        try:
            schema = OpenClawConfig.model_json_schema()
            return schema
        except Exception as e:
            dashboard_logger.error(f"Error generating schema: {e}")
            return {"error": f"生成配置模式失败: {str(e)}"}

    # ===== Exchange Configuration API =====

    @app.get("/api/exchanges")
    async def get_exchanges() -> Dict[str, Any]:
        """Get all exchange configurations."""
        try:
            from openclaw.core.exchange_config import get_exchange_manager
            manager = get_exchange_manager()
            configs = manager.get_all_configs()
            # Return configs without secrets
            return {
                k: {
                    "exchange": v.exchange.value,
                    "name": v.name,
                    "api_key": v.api_key[:4] + "****" + v.api_key[-4:] if len(v.api_key) > 8 else "****",
                    "testnet": v.testnet,
                    "enabled": v.enabled,
                    "created_at": v.created_at.isoformat(),
                    "updated_at": v.updated_at.isoformat(),
                }
                for k, v in configs.items()
            }
        except Exception as e:
            dashboard_logger.error(f"Error loading exchanges: {e}")
            return {}

    @app.post("/api/exchanges")
    async def add_exchange(request: Request) -> Dict[str, Any]:
        """Add a new exchange configuration."""
        try:
            from openclaw.core.exchange_config import get_exchange_manager
            data = await request.json()
            manager = get_exchange_manager()

            config_id = manager.add_config(
                exchange=data["exchange"],
                api_key=data["api_key"],
                secret=data["secret"],
                name=data.get("name", ""),
                passphrase=data.get("passphrase"),
                testnet=data.get("testnet", False),
            )

            dashboard_logger.info(f"Exchange config added: {config_id}")
            return {"success": True, "config_id": config_id}
        except Exception as e:
            dashboard_logger.error(f"Error adding exchange: {e}")
            return {"success": False, "message": str(e)}

    @app.put("/api/exchanges/{config_id}")
    async def update_exchange(config_id: str, request: Request) -> Dict[str, Any]:
        """Update an exchange configuration."""
        try:
            from openclaw.core.exchange_config import get_exchange_manager
            data = await request.json()
            manager = get_exchange_manager()

            config = manager.update_config(
                config_id,
                exchange=data.get("exchange"),
                api_key=data.get("api_key"),
                secret=data.get("secret"),
                name=data.get("name"),
                passphrase=data.get("passphrase"),
                testnet=data.get("testnet"),
            )

            if not config:
                return {"success": False, "message": "配置不存在"}

            dashboard_logger.info(f"Exchange config updated: {config_id}")
            return {"success": True}
        except Exception as e:
            dashboard_logger.error(f"Error updating exchange: {e}")
            return {"success": False, "message": str(e)}

    @app.delete("/api/exchanges/{config_id}")
    async def delete_exchange(config_id: str) -> Dict[str, Any]:
        """Delete an exchange configuration."""
        try:
            from openclaw.core.exchange_config import get_exchange_manager
            manager = get_exchange_manager()

            if manager.delete_config(config_id):
                dashboard_logger.info(f"Exchange config deleted: {config_id}")
                return {"success": True}
            return {"success": False, "message": "配置不存在"}
        except Exception as e:
            dashboard_logger.error(f"Error deleting exchange: {e}")
            return {"success": False, "message": str(e)}

    @app.post("/api/exchanges/{config_id}/test")
    async def test_exchange_connection(config_id: str) -> Dict[str, Any]:
        """Test connection to an exchange."""
        try:
            from openclaw.core.exchange_config import get_exchange_manager
            manager = get_exchange_manager()
            result = manager.test_connection(config_id)
            return result
        except Exception as e:
            dashboard_logger.error(f"Error testing exchange connection: {e}")
            return {"success": False, "message": str(e)}

    @app.get("/api/exchanges/{config_id}/validate")
    async def validate_exchange_config(config_id: str) -> Dict[str, Any]:
        """Validate an exchange configuration."""
        try:
            from openclaw.core.exchange_config import get_exchange_manager
            manager = get_exchange_manager()
            result = manager.validate_config(config_id)
            return result
        except Exception as e:
            dashboard_logger.error(f"Error validating exchange config: {e}")
            return {"valid": False, "errors": [str(e)]}

    # ===== Order Management API =====

    @app.get("/orders")
    async def orders_page(request: Request) -> Any:
        """Serve the order management page."""
        return templates.TemplateResponse("orders.html", {"request": request})

    @app.post("/api/orders")
    async def create_order(order_request: OrderRequest) -> Dict[str, Any]:
        """Create a new trading order."""
        try:
            order = await state_manager.order_manager.create_order(order_request)
            return {
                "status": "success",
                "order": order.model_dump(mode="json"),
            }
        except Exception as e:
            dashboard_logger.error(f"Error creating order: {e}")
            return {"status": "error", "message": str(e)}

    @app.get("/api/orders")
    async def get_orders(
        symbol: Optional[str] = None,
        side: Optional[str] = None,
        status: Optional[str] = None,
        order_type: Optional[str] = None,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        limit: int = 100,
        offset: int = 0,
    ) -> Dict[str, Any]:
        """Get orders with optional filtering."""
        try:
            filters = OrderFilters(
                symbol=symbol,
                side=OrderSide(side) if side else None,
                status=OrderStatus(status) if status else None,
                order_type=OrderType(order_type) if order_type else None,
                start_time=start_time,
                end_time=end_time,
            )
            orders = await state_manager.order_manager.get_orders(filters, limit, offset)
            return {
                "status": "success",
                "orders": [o.model_dump(mode="json") for o in orders],
                "total": len(orders),
            }
        except Exception as e:
            dashboard_logger.error(f"Error getting orders: {e}")
            return {"status": "error", "message": str(e)}

    @app.get("/api/orders/{order_id}")
    async def get_order(order_id: str) -> Dict[str, Any]:
        """Get order details by ID."""
        try:
            order = await state_manager.order_manager.get_order(order_id)
            if not order:
                return {"status": "error", "message": "Order not found"}

            fills = await state_manager.order_manager.get_order_fills(order_id)
            return {
                "status": "success",
                "order": order.model_dump(mode="json"),
                "fills": [f.model_dump(mode="json") for f in fills],
            }
        except Exception as e:
            dashboard_logger.error(f"Error getting order: {e}")
            return {"status": "error", "message": str(e)}

    @app.delete("/api/orders/{order_id}")
    async def cancel_order(order_id: str) -> Dict[str, Any]:
        """Cancel an open order."""
        try:
            order = await state_manager.order_manager.cancel_order(order_id)
            if not order:
                return {"status": "error", "message": "Order not found or cannot be cancelled"}

            return {
                "status": "success",
                "message": "Order cancelled",
                "order": order.model_dump(mode="json"),
            }
        except Exception as e:
            dashboard_logger.error(f"Error cancelling order: {e}")
            return {"status": "error", "message": str(e)}

    @app.get("/api/positions")
    async def get_positions(symbol: Optional[str] = None) -> Dict[str, Any]:
        """Get current positions."""
        try:
            if symbol:
                position = await state_manager.order_manager.get_position(symbol)
                positions = [position] if position else []
            else:
                positions = await state_manager.order_manager.get_all_positions()

            # Update position prices
            await state_manager.order_manager.update_position_prices()

            return {
                "status": "success",
                "positions": [p.model_dump(mode="json") for p in positions],
            }
        except Exception as e:
            dashboard_logger.error(f"Error getting positions: {e}")
            return {"status": "error", "message": str(e)}

    @app.get("/api/account/summary")
    async def get_account_summary() -> Dict[str, Any]:
        """Get account summary with orders and positions."""
        try:
            summary = await state_manager.order_manager.get_account_summary()
            return {
                "status": "success",
                "summary": summary,
            }
        except Exception as e:
            dashboard_logger.error(f"Error getting account summary: {e}")
            return {"status": "error", "message": str(e)}

    @app.post("/api/positions/close")
    async def close_position(request: Request) -> Dict[str, Any]:
        """Close a position by creating an opposite order."""
        try:
            data = await request.json()
            close_request = ClosePositionRequest(
                symbol=data.get("symbol", ""),
                amount=data.get("amount"),
                order_type=OrderType(data.get("order_type", "market")),
                price=data.get("price"),
            )

            exchange_om = get_exchange_order_manager()
            order = await exchange_om.close_position(close_request)

            if not order:
                return {"status": "error", "message": "Position not found or already closed"}

            return {
                "status": "success",
                "message": "Position close order created",
                "order": order.model_dump(mode="json"),
            }
        except Exception as e:
            dashboard_logger.error(f"Error closing position: {e}")
            return {"status": "error", "message": str(e)}

    @app.get("/api/trading/mode")
    async def get_trading_mode() -> Dict[str, Any]:
        """Get current trading mode status."""
        try:
            exchange_om = get_exchange_order_manager()
            status = exchange_om.get_trading_mode_status()
            return {
                "status": "success",
                "mode": status.model_dump(),
            }
        except Exception as e:
            dashboard_logger.error(f"Error getting trading mode: {e}")
            return {"status": "error", "message": str(e)}

    @app.post("/api/trading/mode")
    async def set_trading_mode(request: Request) -> Dict[str, Any]:
        """Set trading mode (paper or live)."""
        try:
            data = await request.json()
            mode_str = data.get("mode", "paper").lower()

            if mode_str not in ["paper", "live"]:
                return {"status": "error", "message": "Invalid mode. Use 'paper' or 'live'"}

            exchange_om = get_exchange_order_manager()
            new_mode = TradingModeEnum.PAPER if mode_str == "paper" else TradingModeEnum.LIVE
            success = exchange_om.set_trading_mode(new_mode)

            if not success:
                return {"status": "error", "message": "Failed to switch to live mode. Check configuration."}

            status = exchange_om.get_trading_mode_status()
            return {
                "status": "success",
                "message": f"Trading mode switched to {mode_str}",
                "mode": status.model_dump(),
            }
        except Exception as e:
            dashboard_logger.error(f"Error setting trading mode: {e}")
            return {"status": "error", "message": str(e)}

    @app.post("/api/trading/mode/toggle")
    async def toggle_trading_mode() -> Dict[str, Any]:
        """Toggle between paper and live trading modes."""
        try:
            exchange_om = get_exchange_order_manager()
            status = exchange_om.toggle_trading_mode()
            return {
                "status": "success",
                "message": f"Trading mode toggled to {status.mode}",
                "mode": status.model_dump(),
            }
        except Exception as e:
            dashboard_logger.error(f"Error toggling trading mode: {e}")
            return {"status": "error", "message": str(e)}

    @app.get("/api/market/data")
    async def get_market_data(symbol: str) -> Dict[str, Any]:
        """Get market data for a symbol."""
        try:
            # Simulated market data - in production this would fetch from exchange
            base_prices = {
                "BTC/USDT": {"price": 65000.0, "change_24h": 2.5, "volume": 1250000000},
                "ETH/USDT": {"price": 3500.0, "change_24h": -1.2, "volume": 890000000},
                "AAPL": {"price": 175.0, "change_24h": 0.8, "volume": 45000000},
                "GOOGL": {"price": 140.0, "change_24h": -0.5, "volume": 23000000},
                "MSFT": {"price": 420.0, "change_24h": 1.2, "volume": 31000000},
                "AMZN": {"price": 180.0, "change_24h": 0.3, "volume": 38000000},
                "TSLA": {"price": 250.0, "change_24h": -2.1, "volume": 52000000},
            }

            symbol_upper = symbol.upper()
            data = base_prices.get(symbol_upper, {"price": 100.0, "change_24h": 0.0, "volume": 1000000})

            # Add some randomness to price for simulation
            import random
            price_variation = random.uniform(-0.001, 0.001)
            current_price = data["price"] * (1 + price_variation)

            return {
                "status": "success",
                "symbol": symbol_upper,
                "price": round(current_price, 2),
                "bid": round(current_price * 0.999, 2),
                "ask": round(current_price * 1.001, 2),
                "change_24h": data["change_24h"],
                "volume_24h": data["volume"],
                "timestamp": datetime.now().isoformat(),
            }
        except Exception as e:
            dashboard_logger.error(f"Error getting market data: {e}")
            return {"status": "error", "message": str(e)}

    @app.get("/trading", response_class=HTMLResponse)
    async def trading_page(request: Request) -> Any:
        """Serve the trading page."""
        return templates.TemplateResponse("trading.html", {"request": request})

    @app.websocket("/ws/market")
    async def market_websocket_endpoint(websocket: WebSocket) -> None:
        """WebSocket endpoint for real-time market data.

        Clients can subscribe to market data streams:
        - Subscribe to ticker: {"action": "subscribe_ticker", "symbol": "BTC/USDT"}
        - Subscribe to orderbook: {"action": "subscribe_orderbook", "symbol": "BTC/USDT"}
        - Subscribe to trades: {"action": "subscribe_trades", "symbol": "BTC/USDT"}
        - Subscribe to klines: {"action": "subscribe_klines", "symbol": "BTC/USDT", "interval": "1m"}
        - Unsubscribe: {"action": "unsubscribe", "symbol": "BTC/USDT", "channel": "ticker"}
        """
        await websocket.accept()
        dashboard_logger.info("Market WebSocket client connected")

        # Track subscriptions for this connection
        subscribed_symbols: set = set()

        async def on_ticker(symbol: str, ticker: Ticker) -> None:
            """Handle ticker updates."""
            try:
                await websocket.send_json({
                    "type": "ticker",
                    "symbol": symbol,
                    "data": {
                        "symbol": ticker.symbol,
                        "bid": ticker.bid,
                        "ask": ticker.ask,
                        "last": ticker.last,
                        "high": ticker.high,
                        "low": ticker.low,
                        "volume": ticker.volume,
                        "timestamp": ticker.timestamp.isoformat(),
                    },
                    "timestamp": datetime.now().isoformat(),
                })
            except Exception:
                pass  # Connection may be closed

        async def on_orderbook(symbol: str, orderbook: OrderBook) -> None:
            """Handle orderbook updates."""
            try:
                await websocket.send_json({
                    "type": "orderbook",
                    "symbol": symbol,
                    "data": {
                        "bids": [[l.price, l.amount] for l in orderbook.bids[:10]],
                        "asks": [[l.price, l.amount] for l in orderbook.asks[:10]],
                        "best_bid": orderbook.best_bid.price if orderbook.best_bid else 0,
                        "best_ask": orderbook.best_ask.price if orderbook.best_ask else 0,
                        "spread": orderbook.spread,
                        "mid_price": orderbook.mid_price,
                        "timestamp": orderbook.timestamp.isoformat(),
                    },
                    "timestamp": datetime.now().isoformat(),
                })
            except Exception:
                pass

        async def on_trade(symbol: str, trade: Trade) -> None:
            """Handle trade updates."""
            try:
                await websocket.send_json({
                    "type": "trade",
                    "symbol": symbol,
                    "data": {
                        "trade_id": trade.trade_id,
                        "price": trade.price,
                        "amount": trade.amount,
                        "side": trade.side,
                        "timestamp": trade.timestamp.isoformat(),
                    },
                    "timestamp": datetime.now().isoformat(),
                })
            except Exception:
                pass

        async def on_kline(symbol: str, kline: KlineData) -> None:
            """Handle kline updates."""
            try:
                await websocket.send_json({
                    "type": "kline",
                    "symbol": symbol,
                    "data": {
                        "interval": kline.interval,
                        "timestamp": kline.timestamp.isoformat(),
                        "open": kline.open,
                        "high": kline.high,
                        "low": kline.low,
                        "close": kline.close,
                        "volume": kline.volume,
                        "quote_volume": kline.quote_volume,
                        "trades": kline.trades,
                        "is_closed": kline.is_closed,
                    },
                    "timestamp": datetime.now().isoformat(),
                })
            except Exception:
                pass

        try:
            while True:
                # Wait for messages from client
                data = await websocket.receive_text()

                try:
                    message = json.loads(data)
                    action = message.get("action")
                    symbol = message.get("symbol")

                    if action == "ping":
                        await websocket.send_json({"type": "pong"})

                    elif action == "subscribe_ticker" and symbol and ws_manager:
                        await ws_manager.subscribe_ticker(symbol, on_ticker)
                        subscribed_symbols.add(f"ticker:{symbol}")
                        await websocket.send_json({
                            "type": "subscribed",
                            "channel": "ticker",
                            "symbol": symbol,
                        })

                    elif action == "subscribe_orderbook" and symbol and ws_manager:
                        await ws_manager.subscribe_orderbook(symbol, on_orderbook)
                        subscribed_symbols.add(f"orderbook:{symbol}")
                        await websocket.send_json({
                            "type": "subscribed",
                            "channel": "orderbook",
                            "symbol": symbol,
                        })

                    elif action == "subscribe_trades" and symbol and ws_manager:
                        await ws_manager.subscribe_trades(symbol, on_trade)
                        subscribed_symbols.add(f"trades:{symbol}")
                        await websocket.send_json({
                            "type": "subscribed",
                            "channel": "trades",
                            "symbol": symbol,
                        })

                    elif action == "subscribe_klines" and symbol and ws_manager:
                        interval = message.get("interval", "1m")
                        await ws_manager.subscribe_klines(symbol, interval, on_kline)
                        subscribed_symbols.add(f"kline:{symbol}:{interval}")
                        await websocket.send_json({
                            "type": "subscribed",
                            "channel": "kline",
                            "symbol": symbol,
                            "interval": interval,
                        })

                    elif action == "unsubscribe" and symbol:
                        channel = message.get("channel", "ticker")
                        if ws_manager:
                            if channel == "ticker":
                                await ws_manager.unsubscribe_ticker(symbol)
                            elif channel == "orderbook":
                                await ws_manager.unsubscribe_orderbook(symbol)
                            elif channel == "trades":
                                await ws_manager.unsubscribe_trades(symbol)
                            elif channel == "kline":
                                interval = message.get("interval", "1m")
                                await ws_manager.unsubscribe_klines(symbol, interval)

                        await websocket.send_json({
                            "type": "unsubscribed",
                            "channel": channel,
                            "symbol": symbol,
                        })

                    elif action == "get_market_data":
                        # Return simulated market data
                        if symbol:
                            result = await get_market_data(symbol)
                            await websocket.send_json({
                                "type": "market_data",
                                "data": result,
                            })

                except json.JSONDecodeError:
                    await websocket.send_json({
                        "type": "error",
                        "message": "Invalid JSON",
                    })
                except Exception as e:
                    dashboard_logger.error(f"Error handling market WebSocket message: {e}")
                    await websocket.send_json({
                        "type": "error",
                        "message": str(e),
                    })

        except WebSocketDisconnect:
            dashboard_logger.info("Market WebSocket client disconnected")
            # Unsubscribe from all
            if ws_manager:
                for subscription in subscribed_symbols:
                    try:
                        parts = subscription.split(":")
                        channel = parts[0]
                        symbol = parts[1]
                        if channel == "ticker":
                            await ws_manager.unsubscribe_ticker(symbol)
                        elif channel == "orderbook":
                            await ws_manager.unsubscribe_orderbook(symbol)
                        elif channel == "trades":
                            await ws_manager.unsubscribe_trades(symbol)
                        elif channel == "kline":
                            interval = parts[2] if len(parts) > 2 else "1m"
                            await ws_manager.unsubscribe_klines(symbol, interval)
                    except Exception:
                        pass
        except Exception as e:
            dashboard_logger.error(f"Market WebSocket error: {e}")

    @app.websocket("/ws")
    async def websocket_endpoint(websocket: WebSocket) -> None:
        """WebSocket endpoint for real-time updates."""
        await state_manager.connect_websocket(websocket)
        try:
            while True:
                # Keep connection alive and handle client messages
                data = await websocket.receive_text()
                try:
                    message = json.loads(data)
                    # Handle client messages if needed
                    if message.get("action") == "ping":
                        await websocket.send_json({"type": "pong"})
                except json.JSONDecodeError:
                    pass
        except WebSocketDisconnect:
            await state_manager.disconnect_websocket(websocket)
        except Exception as e:
            dashboard_logger.error(f"WebSocket error: {e}")
            await state_manager.disconnect_websocket(websocket)

    return app


# Create the application instance
app = create_app()

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)
