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 DeploymentsClient(BaseClient):
    """Service client for Cloud Blueprint Deployments 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 list_deployments(self, **params):
        """List deployments for the current project."""
        url = self.client.get_deployments_path(self.project_name)
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_deployments
        )

    def create_deployment(self, **kwargs):
        """Create deployment.

        All values come from the test layer via kwargs.
        """
        url = self.client.get_deployments_path(self.project_name)
        post_body = json.dumps(kwargs)
        resp, body = self.post(url, post_body)
        return self._parse_response(
            resp, body, self.client.get_schema().create_deployment
        )

    def show_deployment(self, deployment_name):
        """Get deployment details."""
        url = self.client.get_deployment_path(self.project_name, deployment_name)
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().show_deployment
        )

    def update_deployment(self, deployment_name, **kwargs):
        """Patch deployment (update mutable fields)."""
        url = self.client.get_deployment_path(self.project_name, deployment_name)
        patch_body = json.dumps(kwargs)
        resp, body = self.patch(url, patch_body)
        return self._parse_response(
            resp, body, self.client.get_schema().update_deployment
        )

    def delete_deployment(self, deployment_name):
        """Delete deployment (soft delete)."""
        url = self.client.get_deployment_path(self.project_name, deployment_name)
        resp, body = self.delete(url)
        return self._parse_response(
            resp, body, self.client.get_schema().delete_deployment
        )

    def list_deployment_resources(self, deployment_name, **params):
        """List deployment resources (snapshot)."""
        url = self.client.get_deployment_resources_path(
            self.project_name, deployment_name
        )
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_deployment_resources
        )

    def show_deployment_outputs(self, deployment_name, **params):
        """Get deployment outputs (latest applied run)."""
        url = self.client.get_deployment_outputs_path(
            self.project_name, deployment_name
        )
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().show_deployment_outputs
        )

    def destroy_deployment(self, deployment_name, **kwargs):
        """Destroy deployment (queues destroy run)."""
        url = self.client.get_deployment_destroy_path(
            self.project_name, deployment_name
        )
        post_body = json.dumps(kwargs) if kwargs else None
        resp, body = self.post(url, post_body)
        return self._parse_response(
            resp, body, self.client.get_schema().destroy_deployment
        )


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

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

    @abstractmethod
    def get_deployments_path(self, project_name):
        """Return API endpoint path for deployments list/create."""
        pass

    @abstractmethod
    def get_deployment_path(self, project_name, deployment_name):
        """Return API endpoint path for a specific deployment."""
        pass

    @abstractmethod
    def get_deployment_resources_path(self, project_name, deployment_name):
        """Return API endpoint path for deployment resources."""
        pass

    @abstractmethod
    def get_deployment_outputs_path(self, project_name, deployment_name):
        """Return API endpoint path for deployment outputs."""
        pass

    @abstractmethod
    def get_deployment_destroy_path(self, project_name, deployment_name):
        """Return API endpoint path for deployment destroy action."""
        pass
