import json
from abc import ABC, abstractmethod
from urllib import parse as urllib
from lib.common import rest_client
from lib.services.cloud_blueprint.base_client import BaseClient


class GithubClient(BaseClient):
    """Service client for Cloud Blueprint GitHub API"""

    def __init__(self, auth_provider, client, **kwargs):
        super().__init__(auth_provider=auth_provider, **kwargs)
        self.client = client

    def _parse_response(self, resp, body, schema):
        """Parse and validate response."""
        if not body or body.strip() == '':
            body_data = {}
        else:
            body_data = json.loads(body)
        self.validate_response(schema, resp, body_data)
        return rest_client.ResponseBody(resp, body_data)

    def show_oauth_login_url(self, host):
        """Get GitHub OAuth login URL."""
        url = self.client.get_oauth_login_url_path(host)
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().show_oauth_login_url
        )

    def show_oauth_callback(self, host, code, state):
        """GitHub OAuth callback."""
        url = self.client.get_oauth_callback_path(host)
        params = {"code": code, "state": state}
        url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        # Returns text/html
        return rest_client.ResponseBody(resp, body)

    def list_installations(self, host):
        """List GitHub App installations accessible by the user."""
        url = self.client.get_installations_path(host)
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_installations
        )

    def show_app_install_url(self, host):
        """Get GitHub App install URL."""
        url = self.client.get_app_install_url_path(host)
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().show_app_install_url
        )

    def list_repos(self, host):
        """List repositories accessible by the user via GitHub App installations."""
        url = self.client.get_repos_path(host)
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_repos
        )

    def list_branches(self, host, owner, repo, **params):
        """List GitHub branches."""
        url = self.client.get_branches_path(host, owner, repo)
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_branches
        )


class GithubClientInterface(ABC):
    """Interface for cloud-specific implementations."""

    @abstractmethod
    def get_schema(self):
        """Return response schemas module."""
        pass

    @abstractmethod
    def get_oauth_login_url_path(self, host):
        """Return API endpoint path for OAuth login URL."""
        pass

    @abstractmethod
    def get_oauth_callback_path(self, host):
        """Return API endpoint path for OAuth callback."""
        pass

    @abstractmethod
    def get_installations_path(self, host):
        """Return API endpoint path for GitHub installations."""
        pass

    @abstractmethod
    def get_app_install_url_path(self, host):
        """Return API endpoint path for GitHub App install URL."""
        pass

    @abstractmethod
    def get_repos_path(self, host):
        """Return API endpoint path for GitHub repos."""
        pass

    @abstractmethod
    def get_branches_path(self, host, owner, repo):
        """Return API endpoint path for GitHub branches."""
        pass
