"""
MT4 Client for LLM Agent
Connects to Data Service MT4 endpoints to execute trades on MetaTrader 4
"""

import aiohttp
from typing import Dict, Any, Optional, List
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from loguru import logger


class OrderAction(str, Enum):
    """Trade actions"""
    BUY = "BUY"
    SELL = "SELL"
    BUY_LIMIT = "BUY_LIMIT"
    SELL_LIMIT = "SELL_LIMIT"
    BUY_STOP = "BUY_STOP"
    SELL_STOP = "SELL_STOP"


@dataclass
class MT4AccountInfo:
    """MT4 account information"""
    id: str
    name: str
    login: str
    server: str
    platform: str
    account_type: str
    currency: str
    balance: float
    equity: float
    margin: float
    free_margin: float
    margin_level: Optional[float]
    leverage: int
    profit: float
    connected: bool


@dataclass
class MT4Position:
    """Open position on MT4"""
    id: str
    symbol: str
    type: str
    volume: float
    open_price: float
    current_price: float
    stop_loss: Optional[float]
    take_profit: Optional[float]
    profit: float
    swap: float
    open_time: datetime
    comment: str


@dataclass
class TradeResult:
    """Result of a trade operation"""
    success: bool
    order_id: Optional[str] = None
    position_id: Optional[str] = None
    error: Optional[str] = None


class MT4Client:
    """
    Client for MT4 operations via Data Service

    This client communicates with the Data Service's MT4 endpoints
    to manage MetaTrader 4 accounts and execute trades.
    """

    def __init__(self, data_service_url: str = "http://localhost:8001"):
        self.data_service_url = data_service_url
        self.base_url = f"{data_service_url}/api/mt4"
        self._connected = False
        self._account_info: Optional[MT4AccountInfo] = None

    @property
    def is_connected(self) -> bool:
        return self._connected

    @property
    def account_info(self) -> Optional[MT4AccountInfo]:
        return self._account_info

    async def _request(
        self,
        method: str,
        endpoint: str,
        json_data: Optional[Dict] = None,
        params: Optional[Dict] = None
    ) -> Dict[str, Any]:
        """Make HTTP request to Data Service MT4 API"""
        url = f"{self.base_url}{endpoint}"

        try:
            async with aiohttp.ClientSession() as session:
                async with session.request(
                    method=method,
                    url=url,
                    json=json_data,
                    params=params,
                    timeout=aiohttp.ClientTimeout(total=60)
                ) as response:
                    data = await response.json()

                    if response.status >= 400:
                        error_detail = data.get("detail", "Unknown error")
                        raise MT4ClientError(f"MT4 API error: {error_detail}")

                    return data

        except aiohttp.ClientError as e:
            raise MT4ClientError(f"Connection error: {str(e)}")

    async def check_connection(self) -> bool:
        """Check if MT4 is connected via Data Service"""
        try:
            data = await self._request("GET", "/status")
            self._connected = data.get("connected", False)

            if self._connected:
                self._account_info = MT4AccountInfo(
                    id=data.get("account_id", ""),
                    name="",
                    login=data.get("login", ""),
                    server=data.get("server", ""),
                    platform=data.get("platform", ""),
                    account_type=data.get("account_type", ""),
                    currency=data.get("currency", "USD"),
                    balance=data.get("balance", 0),
                    equity=0,
                    margin=0,
                    free_margin=0,
                    margin_level=None,
                    leverage=100,
                    profit=0,
                    connected=True
                )

            return self._connected

        except Exception as e:
            logger.warning(f"MT4 connection check failed: {e}")
            self._connected = False
            return False

    async def connect(self, account_id: str, token: Optional[str] = None) -> bool:
        """
        Connect to MT4 account

        Args:
            account_id: MetaAPI account ID
            token: Optional MetaAPI token (uses env if not provided)

        Returns:
            True if connected successfully
        """
        try:
            payload = {"account_id": account_id}
            if token:
                payload["token"] = token

            data = await self._request("POST", "/connect", json_data=payload)

            self._connected = data.get("connected", False)

            if self._connected:
                logger.info(f"Connected to MT4 account: {data.get('login')} @ {data.get('server')}")

                # Fetch full account info
                await self.get_account_info()

            return self._connected

        except Exception as e:
            logger.error(f"Failed to connect to MT4: {e}")
            raise

    async def disconnect(self) -> bool:
        """Disconnect from MT4 account"""
        try:
            await self._request("POST", "/disconnect")
            self._connected = False
            self._account_info = None
            logger.info("Disconnected from MT4")
            return True
        except Exception as e:
            logger.error(f"Error disconnecting from MT4: {e}")
            return False

    async def get_account_info(self) -> MT4AccountInfo:
        """Get detailed account information"""
        data = await self._request("GET", "/account")

        self._account_info = MT4AccountInfo(
            id=data["id"],
            name=data["name"],
            login=data["login"],
            server=data["server"],
            platform=data["platform"],
            account_type=data["account_type"],
            currency=data["currency"],
            balance=data["balance"],
            equity=data["equity"],
            margin=data["margin"],
            free_margin=data["free_margin"],
            margin_level=data.get("margin_level"),
            leverage=data["leverage"],
            profit=data["profit"],
            connected=data["connected"]
        )

        return self._account_info

    async def get_tick(self, symbol: str) -> Dict[str, Any]:
        """Get current tick (bid/ask) for a symbol"""
        return await self._request("GET", f"/tick/{symbol.upper()}")

    async def get_positions(self) -> List[MT4Position]:
        """Get all open positions"""
        data = await self._request("GET", "/positions")

        positions = []
        for p in data:
            positions.append(MT4Position(
                id=p["id"],
                symbol=p["symbol"],
                type=p["type"],
                volume=p["volume"],
                open_price=p["open_price"],
                current_price=p["current_price"],
                stop_loss=p.get("stop_loss"),
                take_profit=p.get("take_profit"),
                profit=p["profit"],
                swap=p["swap"],
                open_time=datetime.fromisoformat(p["open_time"].replace("Z", "+00:00")),
                comment=p.get("comment", "")
            ))

        return positions

    async def get_orders(self) -> List[Dict[str, Any]]:
        """Get pending orders"""
        data = await self._request("GET", "/orders")
        return data.get("orders", [])

    async def open_trade(
        self,
        symbol: str,
        action: OrderAction,
        volume: float,
        price: Optional[float] = None,
        stop_loss: Optional[float] = None,
        take_profit: Optional[float] = None,
        comment: str = "OrbiQuant LLM"
    ) -> TradeResult:
        """
        Open a new trade

        Args:
            symbol: Trading symbol (e.g., "EURUSD", "XAUUSD")
            action: BUY, SELL, BUY_LIMIT, SELL_LIMIT, BUY_STOP, SELL_STOP
            volume: Position size in lots
            price: Price for pending orders (None for market orders)
            stop_loss: Stop loss price
            take_profit: Take profit price
            comment: Order comment

        Returns:
            TradeResult with order/position ID
        """
        payload = {
            "symbol": symbol.upper(),
            "action": action.value if isinstance(action, OrderAction) else action,
            "volume": volume,
            "comment": comment
        }

        if price is not None:
            payload["price"] = price
        if stop_loss is not None:
            payload["stop_loss"] = stop_loss
        if take_profit is not None:
            payload["take_profit"] = take_profit

        try:
            data = await self._request("POST", "/trade", json_data=payload)

            return TradeResult(
                success=data.get("success", False),
                order_id=data.get("order_id"),
                position_id=data.get("position_id"),
                error=data.get("error")
            )

        except MT4ClientError as e:
            return TradeResult(success=False, error=str(e))

    async def close_position(
        self,
        position_id: str,
        volume: Optional[float] = None
    ) -> TradeResult:
        """
        Close an open position

        Args:
            position_id: Position ID to close
            volume: Partial volume to close (None = close all)

        Returns:
            TradeResult
        """
        params = {}
        if volume is not None:
            params["volume"] = volume

        try:
            data = await self._request(
                "POST",
                f"/positions/{position_id}/close",
                params=params if params else None
            )

            return TradeResult(
                success=data.get("success", False),
                position_id=data.get("position_id"),
                error=data.get("error")
            )

        except MT4ClientError as e:
            return TradeResult(success=False, error=str(e))

    async def modify_position(
        self,
        position_id: str,
        stop_loss: Optional[float] = None,
        take_profit: Optional[float] = None
    ) -> TradeResult:
        """Modify position SL/TP"""
        payload = {}
        if stop_loss is not None:
            payload["stop_loss"] = stop_loss
        if take_profit is not None:
            payload["take_profit"] = take_profit

        try:
            data = await self._request(
                "PUT",
                f"/positions/{position_id}",
                json_data=payload
            )

            return TradeResult(
                success=data.get("success", False),
                position_id=data.get("position_id"),
                error=data.get("error")
            )

        except MT4ClientError as e:
            return TradeResult(success=False, error=str(e))

    async def cancel_order(self, order_id: str) -> TradeResult:
        """Cancel a pending order"""
        try:
            data = await self._request("DELETE", f"/orders/{order_id}")

            return TradeResult(
                success=data.get("success", False),
                order_id=data.get("order_id"),
                error=data.get("error")
            )

        except MT4ClientError as e:
            return TradeResult(success=False, error=str(e))

    async def calculate_margin(
        self,
        symbol: str,
        action: str,
        volume: float,
        price: Optional[float] = None
    ) -> Dict[str, Any]:
        """Calculate required margin for a trade"""
        params = {
            "symbol": symbol.upper(),
            "action": action,
            "volume": volume
        }
        if price:
            params["price"] = price

        return await self._request("POST", "/calculate-margin", params=params)

    async def get_symbols(self) -> List[str]:
        """Get available trading symbols"""
        data = await self._request("GET", "/symbols")
        return data.get("symbols", [])


class MT4ClientError(Exception):
    """MT4 client error"""
    pass


# Singleton instance for service-wide use
_mt4_client: Optional[MT4Client] = None


def get_mt4_client(data_service_url: str = "http://localhost:8001") -> MT4Client:
    """Get or create MT4 client singleton"""
    global _mt4_client
    if _mt4_client is None:
        _mt4_client = MT4Client(data_service_url)
    return _mt4_client
