import json
import base64

from moto.core.responses import BaseResponse
from .models import acm_backends, AWSError, AWSValidationException


class AWSCertificateManagerResponse(BaseResponse):
    @property
    def acm_backend(self):
        """
        ACM Backend

        :return: ACM Backend object
        :rtype: moto.acm.models.AWSCertificateManagerBackend
        """
        return acm_backends[self.region]

    @property
    def request_params(self):
        try:
            return json.loads(self.body)
        except ValueError:
            return {}

    def _get_param(self, param, default=None):
        return self.request_params.get(param, default)

    def add_tags_to_certificate(self):
        arn = self._get_param("CertificateArn")
        tags = self._get_param("Tags")

        if arn is None:
            msg = "A required parameter for the specified action is not supplied."
            return (
                json.dumps({"__type": "MissingParameter", "message": msg}),
                dict(status=400),
            )

        try:
            self.acm_backend.add_tags_to_certificate(arn, tags)
        except AWSError as err:
            return err.response()

        return ""

    def delete_certificate(self):
        arn = self._get_param("CertificateArn")

        if arn is None:
            msg = "A required parameter for the specified action is not supplied."
            return (
                json.dumps({"__type": "MissingParameter", "message": msg}),
                dict(status=400),
            )

        try:
            self.acm_backend.delete_certificate(arn)
        except AWSError as err:
            return err.response()

        return ""

    def describe_certificate(self):
        arn = self._get_param("CertificateArn")

        if arn is None:
            msg = "A required parameter for the specified action is not supplied."
            return (
                json.dumps({"__type": "MissingParameter", "message": msg}),
                dict(status=400),
            )

        try:
            cert_bundle = self.acm_backend.get_certificate(arn)
        except AWSError as err:
            return err.response()

        return json.dumps(cert_bundle.describe())

    def get_certificate(self):
        arn = self._get_param("CertificateArn")

        if arn is None:
            msg = "A required parameter for the specified action is not supplied."
            return (
                json.dumps({"__type": "MissingParameter", "message": msg}),
                dict(status=400),
            )

        try:
            cert_bundle = self.acm_backend.get_certificate(arn)
        except AWSError as err:
            return err.response()

        result = {
            "Certificate": cert_bundle.cert.decode(),
            "CertificateChain": cert_bundle.chain.decode(),
        }
        return json.dumps(result)

    def import_certificate(self):
        """
        Returns errors on:
        Certificate, PrivateKey or Chain not being properly formatted
        Arn not existing if its provided
        PrivateKey size > 2048
        Certificate expired or is not yet in effect

        Does not return errors on:
        Checking Certificate is legit, or a selfsigned chain is provided

        :return: str(JSON) for response
        """
        certificate = self._get_param("Certificate")
        private_key = self._get_param("PrivateKey")
        chain = self._get_param("CertificateChain")  # Optional
        current_arn = self._get_param("CertificateArn")  # Optional
        tags = self._get_param("Tags")  # Optional

        # Simple parameter decoding. Rather do it here as its a data transport decision not part of the
        # actual data
        try:
            certificate = base64.standard_b64decode(certificate)
        except Exception:
            return AWSValidationException(
                "The certificate is not PEM-encoded or is not valid."
            ).response()
        try:
            private_key = base64.standard_b64decode(private_key)
        except Exception:
            return AWSValidationException(
                "The private key is not PEM-encoded or is not valid."
            ).response()
        if chain is not None:
            try:
                chain = base64.standard_b64decode(chain)
            except Exception:
                return AWSValidationException(
                    "The certificate chain is not PEM-encoded or is not valid."
                ).response()

        try:
            arn = self.acm_backend.import_cert(
                certificate, private_key, chain=chain, arn=current_arn, tags=tags
            )
        except AWSError as err:
            return err.response()

        return json.dumps({"CertificateArn": arn})

    def list_certificates(self):
        certs = []
        statuses = self._get_param("CertificateStatuses")
        for cert_bundle in self.acm_backend.get_certificates_list(statuses):
            certs.append(
                {
                    "CertificateArn": cert_bundle.arn,
                    "DomainName": cert_bundle.common_name,
                }
            )

        result = {"CertificateSummaryList": certs}
        return json.dumps(result)

    def list_tags_for_certificate(self):
        arn = self._get_param("CertificateArn")

        if arn is None:
            msg = "A required parameter for the specified action is not supplied."
            return {"__type": "MissingParameter", "message": msg}, dict(status=400)

        try:
            cert_bundle = self.acm_backend.get_certificate(arn)
        except AWSError as err:
            return err.response()

        result = {"Tags": []}
        # Tag "objects" can not contain the Value part
        for key, value in cert_bundle.tags.items():
            tag_dict = {"Key": key}
            if value is not None:
                tag_dict["Value"] = value
            result["Tags"].append(tag_dict)

        return json.dumps(result)

    def remove_tags_from_certificate(self):
        arn = self._get_param("CertificateArn")
        tags = self._get_param("Tags")

        if arn is None:
            msg = "A required parameter for the specified action is not supplied."
            return (
                json.dumps({"__type": "MissingParameter", "message": msg}),
                dict(status=400),
            )

        try:
            self.acm_backend.remove_tags_from_certificate(arn, tags)
        except AWSError as err:
            return err.response()

        return ""

    def request_certificate(self):
        domain_name = self._get_param("DomainName")
        domain_validation_options = self._get_param(
            "DomainValidationOptions"
        )  # is ignored atm
        idempotency_token = self._get_param("IdempotencyToken")
        subject_alt_names = self._get_param("SubjectAlternativeNames")
        tags = self._get_param("Tags")  # Optional

        if subject_alt_names is not None and len(subject_alt_names) > 10:
            # There is initial AWS limit of 10
            msg = (
                "An ACM limit has been exceeded. Need to request SAN limit to be raised"
            )
            return (
                json.dumps({"__type": "LimitExceededException", "message": msg}),
                dict(status=400),
            )

        try:
            arn = self.acm_backend.request_certificate(
                domain_name,
                domain_validation_options,
                idempotency_token,
                subject_alt_names,
                tags,
            )
        except AWSError as err:
            return err.response()

        return json.dumps({"CertificateArn": arn})

    def resend_validation_email(self):
        arn = self._get_param("CertificateArn")
        domain = self._get_param("Domain")
        # ValidationDomain not used yet.
        # Contains domain which is equal to or a subset of Domain
        # that AWS will send validation emails to
        # https://docs.aws.amazon.com/acm/latest/APIReference/API_ResendValidationEmail.html
        # validation_domain = self._get_param('ValidationDomain')

        if arn is None:
            msg = "A required parameter for the specified action is not supplied."
            return (
                json.dumps({"__type": "MissingParameter", "message": msg}),
                dict(status=400),
            )

        try:
            cert_bundle = self.acm_backend.get_certificate(arn)

            if cert_bundle.common_name != domain:
                msg = "Parameter Domain does not match certificate domain"
                _type = "InvalidDomainValidationOptionsException"
                return json.dumps({"__type": _type, "message": msg}), dict(status=400)

        except AWSError as err:
            return err.response()

        return ""

    def export_certificate(self):
        certificate_arn = self._get_param("CertificateArn")
        passphrase = self._get_param("Passphrase")

        if certificate_arn is None:
            msg = "A required parameter for the specified action is not supplied."
            return (
                json.dumps({"__type": "MissingParameter", "message": msg}),
                dict(status=400),
            )

        try:
            (
                certificate,
                certificate_chain,
                private_key,
            ) = self.acm_backend.export_certificate(
                certificate_arn=certificate_arn, passphrase=passphrase,
            )
            return json.dumps(
                dict(
                    Certificate=certificate,
                    CertificateChain=certificate_chain,
                    PrivateKey=private_key,
                )
            )
        except AWSError as err:
            return err.response()
