from abc import ABC, abstractmethod
from typing import Dict, List, Tuple

from aiohttp import ClientResponse

from pyot.utils.logging import LazyLogger


LOGGER = LazyLogger(__name__)


class LimiterToken:

    def __init__(self, server: str, method: str, epoch: float, sleep: float, allowed: List[str], pinging: List[Tuple[str, str, int]]) -> None:
        self.server = server
        self.method = method
        self.epoch = epoch
        self.sleep = sleep
        self.allowed = allowed
        self.pinging = pinging

    def allow(self):
        return self.sleep == 0


class BaseLimiter(ABC):

    def parse_headers(self, response: ClientResponse) -> Dict[str, List[List[int]]]:
        if response is None:
            return None
        headers = response.headers if hasattr(response, "headers") else None
        if headers is None:
            return None
        try:
            app_limit = [[int(val) for val in token.split(':')] for token in headers["X-App-Rate-Limit"].split(',')]
            app_count = [[int(val) for val in token.split(':')] for token in headers["X-App-Rate-Limit-Count"].split(',')]
            method_limit = [[int(val) for val in token.split(':')] for token in headers["X-Method-Rate-Limit"].split(',')]
            method_count = [[int(val) for val in token.split(':')] for token in headers["X-Method-Rate-Limit-Count"].split(',')]
            return {
                "app_limit": app_limit,
                "app_count": app_count,
                "method_limit": method_limit,
                "method_count": method_count,
            }
        except KeyError:
            return None

    def parse_429(self, response: ClientResponse) -> Dict:
        if response is None:
            return None
        headers = response.headers if hasattr(response, "headers") else None
        if headers is None:
            return None
        return {
            "type": response.headers["X-Rate-Limit-Type"] if "X-Rate-Limit-Type" in response.headers else "service",
            "time": float(response.headers["Retry-After"]) if "Retry-After" in response.headers else 1,
        }

    @abstractmethod
    def __init__(self, game: str, api_key: str, limiting_share: int = 1):
        self.game = game
        self.api_key = api_key
        self.limiting_share = limiting_share

    @abstractmethod
    async def get_token(self, server: str, method: str) -> LimiterToken:
        pass

    @abstractmethod
    async def sync_rates(self, token: LimiterToken, response: ClientResponse):
        pass

    @abstractmethod
    async def ping_fail(self, token: LimiterToken):
        pass

    @abstractmethod
    async def freeze_rates(self, token: LimiterToken, response: ClientResponse):
        pass
