# -*- encoding: utf-8 -*-
import ssl
from urllib.parse import urlparse

import requests
from suite_py.__version__ import __version__
from suite_py.lib.handler.github_handler import GithubHandler
from suite_py.lib.handler.okta_handler import Okta
from suite_py.lib import logger


class BackstageError(Exception): ...


class Backstage:
    _okta: Okta
    _github: GithubHandler

    def __init__(self, config, okta: Okta, tokens=None):
        self._backstage_url = config.user["backstage_url"]
        self._timeout = config.user["backstage_timeout"]
        self._backstage_identity_token = None

        # Under mise cacert are loaded from ~/.local/share/mise
        # so we need to force system defaults to make warp happy
        self._verify = ssl.get_default_verify_paths().capath
        self._okta = okta

        if tokens is not None:
            self._github = GithubHandler(tokens)

    def lock_project(self, project, env):
        return self._request(
            method="post",
            url=self._entity_locking_endpoint("/projects/manage-lock"),
            headers=self._okta_headers(),
            raise_for_status=True,
            json={
                "project": project,
                "status": "locked",
                "user": self._get_user(),
                "environment": env,
            },
        )

    def unlock_project(self, project, env):
        return self._request(
            method="post",
            url=self._entity_locking_endpoint("/projects/manage-lock"),
            headers=self._okta_headers(),
            raise_for_status=True,
            json={
                "project": project,
                "status": "unlocked",
                "user": self._get_user(),
                "environment": env,
            },
        )

    def status(self, project, env):
        return self._request(
            method="get",
            url=self._entity_locking_endpoint(
                f"/projects/check?project={project}&environment={env}"
            ),
            raise_for_status=True,
        )

    def check(self):
        response = self._request(
            method="get",
            url=self._entity_locking_endpoint("/ping"),
            headers=self._okta_headers(),
            timeout=(2, self._timeout),
            raise_for_status=True,
        )

        assert response.text == "pong"

    def get_users_list(self):
        response = self._fetch_users()

        if response.status_code == 401:
            self._refresh_backstage_identity_token()
            response = self._fetch_users()

        response.raise_for_status()

        return [
            {"name": u.pop("displayName"), **u}
            for u in (item["spec"]["profile"] for item in response.json()["items"])
            if "github" in u and "youtrack" in u and "displayName" in u
        ]

    def _request(self, method, url, **kwargs):
        timeout = kwargs.pop("timeout", self._timeout)
        verify = kwargs.pop("verify", self._verify)
        allow_redirects = kwargs.pop("allow_redirects", False)
        raise_for_status = kwargs.pop("raise_for_status", False)

        response = requests.request(
            method=method,
            url=url,
            timeout=timeout,
            verify=verify,
            allow_redirects=allow_redirects,
            **kwargs,
        )

        redirect = (
            urlparse(response.headers["Location"])
            if response.status_code == 302
            else None
        )

        if redirect is not None and redirect.netloc == "prima.cloudflareaccess.com":
            logger.error(
                "It looks like Cloudflare WARP is not active, please enable it and try again"
            )

            raise BackstageError("Cloudflare Access authentication redirect")

        if raise_for_status:
            response.raise_for_status()

        return response

    def _entity_locking_endpoint(self, path):
        return f"{self._backstage_url}{path}"

    def _get_user(self):
        return self._github.get_user().login

    def _okta_headers(self):
        return {
            "User-Agent": f"suite-py/{__version__}",
            "Authorization": f"Bearer {self._okta.get_id_token()}",
        }

    def _backstage_headers(self):
        if self._backstage_identity_token is None:
            self._refresh_backstage_identity_token()

        return {
            "User-Agent": f"suite-py/{__version__}",
            "Authorization": f"Bearer {self._backstage_identity_token}",
        }

    def _fetch_users(self):
        fields = ",".join(
            f"spec.profile.{f}" for f in ("displayName", "youtrack", "slack", "github")
        )

        return self._request(
            method="GET",
            url=f"{self._backstage_url}/api/catalog/entities/by-query?filter=kind=user&fields={fields}&limit=500",
            headers=self._backstage_headers(),
        )

    def _refresh_backstage_identity_token(self):
        response = self._request(
            method="GET",
            url=f"{self._backstage_url}/api/auth/cfaccess/refresh",
            raise_for_status=True,
        )

        self._backstage_identity_token = response.json()["backstageIdentity"]["token"]
