import requests
from .constants import base_url
import time
import math
from threading import Condition
from .exceptions import ScopeError


class HTTPHandler:
    def __init__(self, auth):
        self.auth = auth
        self.rate_limit = RateLimiter()

    def get_headers(self, **kwargs):
        headers = {
            'Authorization': f'Bearer {self.auth.token}',
            **{str(key): str(value) for key, value in kwargs.items() if value is not None}
        }
        return headers

    def __getattr__(self, method):
        if self.auth.expire_time <= time.time():
            self.auth.refresh_access_token()
        if not self.rate_limit.can_request:
            wait = Condition()
            wait.wait_for(self.rate_limit.get_can_request)

        def func(client, path, data=None, **kwargs):
            if data is None:
                data = {}
            scope_required = path.scope
            if client.auth.scope < scope_required:
                raise ScopeError("You don't have the right scope to be able to do this.")
            headers = self.get_headers(**kwargs)
            response = getattr(requests, method)(base_url + path.path, headers=headers, data=data)
            response.raise_for_status()
            return response.json()
        return func


class RateLimiter:
    def __init__(self):
        self.offset = time.perf_counter() % 60
        self.minute_counter = 0
        self.rate_counter = 0

    def reset(self):
        if math.floor(self.time/60) != self.minute_counter:
            self.minute_counter = math.floor(self.time/60)
            self.rate_counter = 0

    def request_used(self):
        self.rate_counter += 1

    # Made a function to be used with threading.wait_for
    def get_can_request(self):
        return self.can_request

    @property
    def can_request(self):
        return self.rate_counter != 60

    @property
    def time(self):
        return time.perf_counter()-self.offset
