from requests import Session
from urllib.parse import urlencode
import time


class Credentials:
    base_url: str
    username: str
    password: str
    domain: str
    home_domain_id: int
    auth_token: str

    def __init__(
        self,
        base_url: str,
        username: str,
        password: str,
        domain: str,
        home_domain_id: int,
        auth_token: str = "",
    ):
        self.base_url = base_url
        self.username = username
        self.password = password
        self.domain = domain
        self.home_domain_id = home_domain_id
        self.auth_token = auth_token

    def store_auth_token(self, auth_response: dict) -> str:
        login_token = (
            auth_response.get("response", {}).get("user", {}).get("token", None)
        )

        if login_token is None:
            error_code = auth_response.get("response", {}).get("code", "Generic Error")
            error_message = auth_response.get("response", {}).get(
                "message", "Generic Error"
            )
            raise Exception(f"{error_code}: {error_message}")

        self.auth_token = login_token
        return login_token


class Connection:
    credentials: Credentials
    session: Session

    def __init__(
        self,
        credentials: Credentials,
        session: Session = Session(),
    ):
        self.credentials = credentials
        self.session = session

    def __construct_login_payload(self):
        return {
            "request": {
                "cmd": "login3",
                "username": f"{self.credentials.domain}/{self.credentials.username}",
                "password": self.credentials.password,
            }
        }

    def authenticate(self):
        response = self.session.post(
            self.credentials.base_url, json=self.__construct_login_payload()
        )
        response.raise_for_status()
        self.session.headers.update(
            {
                "Authorization": f"Bearer {self.credentials.store_auth_token(response.json())}"
            }
        )

    def get(self, cmd: str, params: dict = {}):
        query_string = urlencode({"cmd": cmd} | params, doseq=True)

        response = self.session.get(f"{self.credentials.base_url}?{query_string}")
        response.raise_for_status()

        if response.status_code == 502:
            time.sleep(10)
            self.authenticate()
            return self.get(cmd, params)
        elif response.status_code == 429:
            time.sleep(60)
            self.authenticate()
            return self.get(cmd, params)
        elif response.status_code != 200:
            raise Exception(f"Error: {response.status_code} - {response.text}")

        return response.json()

    def post(self, cmd: str | None, params: dict = {}, payload: dict = {}):
        query_string = (
            urlencode({"cmd": cmd} | params, doseq=True)
            if cmd
            else urlencode(params, doseq=True)
        )

        target_url = (
            f"{self.credentials.base_url}?{query_string}"
            if query_string
            else self.credentials.base_url
        )

        # NEED TO DO A POST PAYLOAD WITH THE TOKEN ADDED TO THE REQUESTS OBJECT

        response = self.session.post(target_url, json=payload)
        response.raise_for_status()

        if response.status_code == 429:
            time.sleep(60)
            self.authenticate()
            return self.post(cmd, params, payload)
        if response.status_code == 502:
            time.sleep(10)
            self.authenticate()
            return self.post(cmd, params, payload)
        elif response.status_code != 200:
            raise Exception(f"Error: {response.status_code} - {response.text}")

        return response.json()
