import asyncio
import time
import aiohttp
from datetime import datetime
from typing import Any, Dict, Literal, Optional


from ksxt.base.errors import NotSupportedError
from ksxt.base.rate_limiter import RateLimiterContext
from ksxt.base.rest_exchange import RestExchange
from ksxt.config import VALID_METHODS
import ksxt.models


class AsyncExchange(RestExchange):
    synchronous = False

    def __init__(self, config: Dict = None, filename: str = None):
        super().__init__(config, filename)

        self.asyncio_loop = None
        self.session: aiohttp.ClientSession = None

    async def initialize(self):
        if self.asyncio_loop is None:
            self.asyncio_loop = asyncio.get_event_loop()

        if self.session is None or (
            self.session_last_used and (time.time() - self.session_last_used > self.session_lifetime)
        ):
            if self.session:
                await self.session.close()
            self.session = aiohttp.ClientSession()
            self.session_last_used = time.time()

    async def close(self):
        if self.session:
            await self.session.close()
            self.session = None
            self.session_last_used = None

    async def __aenter__(self):
        await self.initialize()
        return self

    async def __aexit__(self, *args):
        await self.close()

    async def fetch(self, url, method="GET", headers=None, body=None, params=None):
        # Ensure that resources are initialized before the request
        await self.initialize()

        method_lower = method.lower()
        if method_lower not in VALID_METHODS:
            raise ValueError(f"Invalid HTTP method: {method}")

        session_method = getattr(self.session, method.lower())

        # TODO : Set rate limiters value when config load
        api_name = ""

        if api_name and api_name in self.rate_limiters:
            await self.rate_limiters[api_name].async_acquire()

        try:
            async with session_method(
                url,
                headers=headers,
                data=str(body).encode() if body else None,
                params=params,
                timeout=aiohttp.ClientTimeout(total=int(self.timeout / 1000)),
            ) as response:
                http_response = await response.text(errors="replace")
                json_response = self.parse_json(http_response)
                return json_response
        except (asyncio.TimeoutError, aiohttp.ClientError) as e:
            details = f"{self.id} {method} {url}"
            raise TimeoutError(details) from e
        finally:
            if (
                api_name
                and api_name in self.rate_limiters
                and isinstance(self.rate_limiters[api_name], RateLimiterContext)
            ):
                self.rate_limiters[api_name].release()

    async def fetch2(
        self, path, security_type, params={}, headers: Any | None = None, body: Any | None = None, config={}
    ):
        is_activate = self.apis[self.type][security_type][path]["activate"]
        if not is_activate:
            return {
                "response": {
                    # 성공 실패 여부
                    "success": "-1",
                    # 응답코드
                    "code": "fail",
                    # 응답메세지
                    "message": f"지원하지 않는 함수({path}) 입니다.",
                }
            }

        # if self.enableRateLimit:
        #     cost = self.calculate_rate_limiter_cost(api, method, path, params, config)
        #     self.throttle(cost)

        # self.lastRestRequestTimestamp = self.milliseconds()

        method_type = self.apis[self.type][security_type][path]["method"]
        api_type = self.apis[self.type][security_type][path]["api"]
        request = self.sign(path, security_type, method_type, api_type, headers, body, params, config)
        return await self.fetch(
            request["url"], request["method"], request["headers"], request["body"], request["params"]
        )

    # region base method
    async def fetch_markets(self, market_name: str) -> ksxt.models.KsxtMarketResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_markets.__qualname__}() is not supported yet.")

    async def fetch_security(self, symbol: str, base_market: str = "KRW") -> ksxt.models.KsxtSecurityResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_security.__qualname__}() is not supported yet.")

    async def fetch_ticker(self, symbol: str, base_market: str = "KRW") -> ksxt.models.KsxtTickerResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_ticker.__qualname__}() is not supported yet.")

    async def fetch_orderbook(self, symbol: str, base_market: str = "KRW") -> ksxt.models.KsxtSingleOrderBookResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_orderbook.__qualname__}() is not supported yet.")

    async def fetch_historical_data(
        self,
        symbol: str,
        time_frame: str,
        start: datetime | None = None,
        end: datetime | None = None,
        base_market: str = "KRW",
    ) -> ksxt.models.KsxtHistoricalDataResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_historical_data.__qualname__}() is not supported yet.")

    async def fetch_is_holiday(self, dt: datetime, base_market: str = "KRW"):
        raise NotSupportedError(f"{self.id} {self.fetch_is_holiday.__qualname__}() is not supported yet.")

    async def fetch_user_info(self, base_market: str = "KRW"):
        raise NotSupportedError(f"{self.id} {self.fetch_user_info.__qualname__}() is not supported yet.")

    async def fetch_balance(
        self, acc_num: str, base_market: str = "KRW", excluded_symbols: list[str] | None = None
    ) -> ksxt.models.KsxtBalanceResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_balance.__qualname__}() is not supported yet.")

    async def fetch_cash(self, acc_num: str, base_market: str = "KRW") -> ksxt.models.KsxtCashResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_cash.__qualname__}() is not supported yet.")

    async def fetch_screener_list(self, base_market: str = "KRW"):
        raise NotSupportedError(f"{self.id} {self.fetch_screener_list.__qualname__}() is not supported yet.")

    async def fetch_screener(self, screen_id: str, base_market: str = "KRW"):
        raise NotSupportedError(f"{self.id} {self.fetch_screener.__qualname__}() is not supported yet.")

    async def fetch_deposit_history(
        self, acc_num: str, start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
    ) -> ksxt.models.KsxtDepositHistoryResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_deposit_history.__qualname__}() is not supported yet.")

    async def fetch_withdrawal_history(
        self, acc_num: str, start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
    ) -> ksxt.models.KsxtWithdrawalHistoryResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_withdrawal_history.__qualname__}() is not supported yet.")

    async def create_order(
        self,
        acc_num: str,
        symbol: str,
        ticket_type: Literal["EntryLong", "EntryShort", "ExitLong", "ExitShort"],
        otype: Literal["limit", "market"],
        price: float | None = 0,
        qty: float | None = 0,
        amount: float | None = 0,
        base_market: str = "KRW",
    ) -> ksxt.models.KsxtCreateOrderResponse:
        raise NotSupportedError(f"{self.id} {self.create_order.__qualname__}() is not supported yet.")

    async def cancel_order(
        self, acc_num: str, order_id: str, symbol: str | None = "", qty: float = 0, *args, base_market: str = "KRW"
    ) -> ksxt.models.KsxtCancelOrderResponse:
        raise NotSupportedError(f"{self.id} {self.cancel_order.__qualname__}() is not supported yet.")

    async def modify_order(
        self,
        acc_num: str,
        order_id: str,
        price: float,
        qty: float,
        *args,
        symbol: str | None = "",
        base_market: str = "KRW",
    ):
        raise NotSupportedError(f"{self.id} {self.modify_order.__qualname__}() is not supported yet.")

    async def fetch_open_order(
        self,
        acc_num: str,
        symbol: str | None = "",
        start: datetime | None = None,
        end: datetime | None = None,
        base_market: str = "KRW",
    ) -> ksxt.models.KsxtOpenOrderResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_open_order.__qualname__}() is not supported yet.")

    async def fetch_closed_order(
        self,
        acc_num: str,
        symbol: str | None = "",
        start: datetime | None = None,
        end: datetime | None = None,
        base_market: str = "KRW",
    ) -> ksxt.models.KsxtClosedOrderResponse:
        raise NotSupportedError(f"{self.id} {self.fetch_closed_order.__qualname__}() is not supported yet.")

    async def reserve_order(
        self, acc_num: str, symbol: str, price: float, qty: float, target_date: str, base_market: str = "KRW"
    ):
        raise NotSupportedError(f"{self.id} {self.reserve_order.__qualname__}() is not supported yet.")

    # endregion base method

    async def sleep(self, milliseconds):
        return await asyncio.sleep(milliseconds / 1000)
