from datetime import datetime

from finam_trade_api import Client, TokenManager, ErrorModel
from finam_trade_api.account import GetTransactionsRequest
from finam_trade_api.assets import AssetsResponse
from finam_trade_api.base_client import BaseClient
from finam_trade_api.instruments import TimeFrame, TradesResponse, OrderBookResponse, QuoteResponse, \
    BarsResponse
from mcp.server.fastmcp.exceptions import ToolError

from src.tradeapi.models import GetTradesRequest, AssetParamsResponse, GetAccountResponse
from src.tradeapi.order.orders import OrderClient


class FinamClient:
    def __init__(self, api_key, account_id):
        token_manager = TokenManager(api_key)
        self.client = Client(token_manager)
        self.client.orders = OrderClient(token_manager)  # доделка
        self.account_id = account_id

    @classmethod
    async def create(cls, api_key, account_id):
        instance = cls(api_key, account_id)
        await instance.client.access_tokens.set_jwt_token()
        return instance

    """ Helper """

    async def _exec_request(self, client: BaseClient, method: BaseClient.RequestMethod, url: str, **kwargs) -> dict:
        response, ok = await client._exec_request(method, url, **kwargs)

        if not ok:
            err = ErrorModel(**response)
            raise ToolError(f"code={err.code} | message={err.message} | details={err.details}")
        return response

    """ Аккаунт """

    async def get_account_info(self):
        account_client = self.client.account
        return GetAccountResponse(**await self._exec_request(account_client, BaseClient.RequestMethod.GET,
                                                             f"{account_client._url}/{self.account_id}"))

    async def get_transactions(self, start_time: datetime, end_time: datetime, limit: int = 10):
        return await self.client.account.get_transactions(
            GetTransactionsRequest(account_id=self.account_id, start_time=start_time.isoformat(),
                                   end_time=end_time.isoformat(), limit=limit))

    async def get_trades(self, start_time: datetime, end_time: datetime, limit: int = 10):
        return await self.client.account.get_trades(
            GetTradesRequest(account_id=self.account_id, start_time=start_time.isoformat(),
                             end_time=end_time.isoformat(), limit=limit))

    """ Assets """

    async def get_assets(self):
        assets_client = self.client.assets
        return AssetsResponse(
            **await self._exec_request(assets_client, BaseClient.RequestMethod.GET, f"{assets_client._url}"))

    async def get_asset(self, symbol: str):
        return await self.client.assets.get_asset(symbol, self.account_id)

    async def get_asset_params(self, symbol: str):
        assets_client = self.client.assets
        return AssetParamsResponse(**await self._exec_request(assets_client, BaseClient.RequestMethod.GET,
                                                              f"{assets_client._url}/{symbol}/params",
                                                              params={"account_id": self.account_id}))

    async def get_exchanges(self):
        return await self.client.assets.get_exchanges()

    async def get_options_chain(self, underlying_symbol: str):
        return await self.client.assets.get_options_chain(underlying_symbol)

    async def get_schedule(self, symbol: str):
        return await self.client.assets.get_schedule(symbol)

    """ Market Data """

    async def get_bars(self, symbol: str, start_time: datetime, end_time: datetime,
                       timeframe: TimeFrame):
        market_client = self.client.instruments
        return BarsResponse(**await self._exec_request(market_client, BaseClient.RequestMethod.GET,
                                                       f"{market_client._url}/{symbol}/bars",
                                                       params={
                                                           "timeframe": timeframe.value,
                                                           "interval.start_time": start_time.isoformat(),
                                                           "interval.end_time": end_time.isoformat(),
                                                       }, ))

    async def get_last_quote(self, symbol: str):
        market_client = self.client.instruments
        return QuoteResponse(**await self._exec_request(market_client, BaseClient.RequestMethod.GET,
                                                        f"{market_client._url}/{symbol}/quotes/latest", ))

    async def get_last_trades(self, symbol: str):
        market_client = self.client.instruments
        return TradesResponse(**await self._exec_request(market_client, BaseClient.RequestMethod.GET,
                                                         f"{market_client._url}/{symbol}/trades/latest", ))

    async def get_order_book(self, symbol: str):
        market_client = self.client.instruments
        return OrderBookResponse(**await self._exec_request(market_client, BaseClient.RequestMethod.GET,
                                                            f"{market_client._url}/{symbol}/orderbook", ))

    """ Orders """

    async def get_orders(self):
        """Получение списка заявок для аккаунта"""
        return await self.client.orders.get_orders(self.account_id)

    async def get_order(self, order_id: str):
        """Получение информации о конкретном ордере"""
        return await self.client.orders.get_order(order_id, self.account_id)

    async def place_order(self, order):
        """Выставление биржевой заявки"""
        return await self.client.orders.place_order(order, self.account_id)

    async def cancel_order(self, order_id: str):
        """Отмена биржевой заявки"""
        return await self.client.orders.cancel_order(order_id, self.account_id)
