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


class KeysetsClient(BaseClient):
    """Service client for API Gateway Keysets resource."""

    def __init__(self, auth_provider, service, region, client=None, **kwargs):
        super(KeysetsClient, self).__init__(auth_provider, service, region, **kwargs)
        self.client = client

    def _build_url(self, keyset_id=None):
        """Build URL for keyset operations."""
        url = self.client.get_keysets_path(self.project_name)
        if keyset_id:
            url = f"{url}/{keyset_id}"
        return url

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

    def list_keysets(self, **params):
        """List keysets."""
        url = self._build_url()
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_keysets
        )

    def create_keyset(self, **kwargs):
        """Create a keyset."""
        url = self._build_url()
        post_body = json.dumps(kwargs)
        resp, body = self.post(url, post_body)
        return self._parse_response(
            resp, body, self.client.get_schema().create_keyset
        )

    def show_keyset(self, keyset_id):
        """Show keyset details."""
        url = self._build_url(keyset_id)
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().show_keyset
        )

    def update_keyset(self, keyset_id, if_match=None, **kwargs):
        """Update a keyset."""
        url = self._build_url(keyset_id)
        headers = {"If-Match": if_match} if if_match else None
        patch_body = json.dumps(kwargs)
        resp, body = self.patch(url, patch_body, headers=headers, extra_headers=True)
        return self._parse_response(
            resp, body, self.client.get_schema().update_keyset
        )

    def delete_keyset(self, keyset_id, if_match=None):
        """Delete a keyset."""
        url = self._build_url(keyset_id)
        headers = {"If-Match": if_match} if if_match else None
        resp, body = self.delete(url, headers=headers, extra_headers=True)
        return self._parse_response(
            resp, body, self.client.get_schema().delete_keyset
        )

    def list_keyset_keys(self, keyset_id, **params):
        """List keys in a keyset."""
        url = self.client.get_keyset_keys_path(self.project_name, keyset_id)
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_keyset_keys
        )

    def update_keyset_keys(self, keyset_id, **kwargs):
        """Batch update keys in a keyset."""
        url = self.client.get_keyset_keys_path(self.project_name, keyset_id)
        patch_body = json.dumps(kwargs)
        resp, body = self.patch(url, patch_body)
        return self._parse_response(
            resp, body, self.client.get_schema().update_keyset_keys
        )

    def list_keyset_stages(self, keyset_id, **params):
        """List stages using a keyset."""
        url = self.client.get_keyset_stages_path(self.project_name, keyset_id)
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_keyset_stages
        )


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

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

    @abstractmethod
    def get_keysets_path(self, project):
        """Return API endpoint path."""
        pass

    @abstractmethod
    def get_keyset_keys_path(self, project, keyset_id):
        """Return API endpoint path for keyset keys."""
        pass

    @abstractmethod
    def get_keyset_stages_path(self, project, keyset_id):
        """Return API endpoint path for keyset stages."""
        pass
