import json

try:
    aliyun_dependencies = True
    from aliyunsdkcore import client
    from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest
    from aliyunsdkalidns.request.v20150109 import AddDomainRecordRequest
    from aliyunsdkalidns.request.v20150109 import DeleteDomainRecordRequest
except ImportError:
    aliyun_dependencies = False

from . import common


class _ResponseForAliyun(object):
    """
    wrapper aliyun resp to the format sewer wanted.
    """

    def __init__(self, status_code=200, content=None, headers=None):
        self.status_code = status_code
        self.headers = headers or {}
        self.content = content or {}
        self.content = json.dumps(content)
        super(_ResponseForAliyun, self).__init__()

    def json(self):
        return json.loads(self.content)


class AliyunDns(common.BaseDns):
    def __init__(self, key, secret, endpoint="cn-beijing", debug=False):
        """
        aliyun dns client
        :param str key: access key
        :param str secret: access sceret
        :param str endpoint: endpoint
        :param bool debug: if debug?
        """
        super(AliyunDns, self).__init__()
        if not aliyun_dependencies:
            raise ImportError(
                """You need to install aliyunDns dependencies. run; pip3 install sewer[aliyun]"""
            )
        self._key = key
        self._secret = secret
        self._endpoint = endpoint
        self._debug = debug
        self.clt = client.AcsClient(self._key, self._secret, self._endpoint, debug=self._debug)

    def _send_reqeust(self, request):
        """
        send request to aliyun
        """
        request.set_accept_format("json")
        try:
            status, headers, result = self.clt.implementation_of_do_action(request)
            if isinstance(result, bytes):
                result = result.decode()
            result = json.loads(result)
            if "Message" in result or "Code" in result:
                result["Success"] = False
                self.logger.warning("aliyundns resp error: %s", result)
        except Exception as exc:
            self.logger.warning("aliyundns failed to send request: %s, %s", str(exc), request)
            status, headers, result = 502, {}, b'{"Success": false}'
            result = json.loads(result)

        if self._debug:
            self.logger.info("aliyundns request name: %s", request.__class__.__name__)
            self.logger.info("aliyundns request query: %s", request.get_query_params())
        return _ResponseForAliyun(status, result, headers)

    def query_recored_items(self, host, zone=None, tipe=None, page=1, psize=200):
        """
        query recored items.
        :param str host: like example.com
        :param str zone: like menduo.example.com
        :param str tipe: TXT, CNAME, IP or other
        :param int page:
        :param int psize:
        :return dict: res = {
                'DomainRecords':
                    {'Record': [
                        {
                            'DomainName': 'menduo.net',
                            'Line': 'default',
                            'Locked': False,
                            'RR': 'zb',
                            'RecordId': '3989515483698964',
                            'Status': 'ENABLE',
                            'TTL': 600,
                            'Type': 'A',
                            'Value': '127.0.0.1',
                            'Weight': 1
                        },
                        {
                            'DomainName': 'menduo.net',
                            'Line': 'default',
                            'Locked': False,
                            'RR': 'a.sub',
                            'RecordId': '3989515480778964',
                            'Status': 'ENABLE',
                            'TTL': 600,
                            'Type': 'CNAME',
                            'Value': 'h.p.menduo.net',
                            'Weight': 1
                        }
                    ]
                    },
                'PageNumber': 1,
                'PageSize': 20,
                'RequestId': 'FC4D02CD-EDCC-4EE8-942F-1497CCC3B10E',
                'TotalCount': 95
            }
        """
        request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
        request.get_action_name()
        request.set_DomainName(host)
        request.set_PageNumber(page)
        request.set_PageSize(psize)
        if zone:
            request.set_RRKeyWord(zone)
        if tipe:
            request.set_TypeKeyWord(tipe)
        resp = self._send_reqeust(request)
        body = resp.json()
        return body

    def query_recored_id(self, root, zone, tipe="TXT"):
        """
        find recored
        :param str root: root host, like example.com
        :param str zone: sub zone, like menduo.example.com
        :param str tipe: record tipe, TXT, CNAME, IP. we use TXT
        :return str:
        """
        record_id = None
        recoreds = self.query_recored_items(root, zone, tipe=tipe)
        recored_list = recoreds.get("DomainRecords", {}).get("Record", [])
        recored_item_list = [i for i in recored_list if i["RR"] == zone]
        if len(recored_item_list):
            record_id = recored_item_list[0]["RecordId"]
        return record_id

    @staticmethod
    def extract_zone(domain_name):
        """
        extract domain to root, sub, acme_txt
        :param str domain_name: the value sewer client passed in, like *.menduo.example.com
        :return tuple: root, zone, acme_txt
        """
        if domain_name.count(".") > 1:
            zone, middle, last = str(domain_name).rsplit(".", 2)
            root = ".".join([middle, last])
            acme_txt = "_acme-challenge.%s" % zone
        else:
            zone = ""
            root = domain_name
            acme_txt = "_acme-challenge"
        return root, zone, acme_txt

    def create_dns_record(self, domain_name, domain_dns_value):
        """
        create a dns record
        :param str domain_name: the value sewer client passed in, like *.menduo.example.com
        :param str domain_dns_value: the value sewer client passed in.
        :return _ResponseForAliyun:
        """
        self.logger.info("create_dns_record start: %s", (domain_name, domain_dns_value))
        root, _, acme_txt = self.extract_zone(domain_name)

        request = AddDomainRecordRequest.AddDomainRecordRequest()
        request.set_DomainName(root)
        request.set_TTL(600)
        request.set_RR(acme_txt)
        request.set_Type("TXT")
        request.set_Value(domain_dns_value)
        resp = self._send_reqeust(request)

        self.logger.info("create_dns_record end: %s", (domain_name, domain_dns_value, resp.json()))

        return resp

    def delete_dns_record(self, domain_name, domain_dns_value):
        """
        delete a txt record we created just now.
        :param str domain_name: the value sewer client passed in, like *.menduo.example.com
        :param str domain_dns_value: the value sewer client passed in. we do not use this.
        :return _ResponseForAliyun:
        :return:
        """
        self.logger.info("delete_dns_record start: %s", (domain_name, domain_dns_value))

        root, _, acme_txt = self.extract_zone(domain_name)

        record_id = self.query_recored_id(root, acme_txt)
        if not record_id:
            msg = "failed to find record_id of domain: %s, value: %s", domain_name, domain_dns_value
            self.logger.warning(msg)
            return

        self.logger.info("start to delete dns record, id: %s", record_id)

        request = DeleteDomainRecordRequest.DeleteDomainRecordRequest()
        request.set_RecordId(record_id)
        resp = self._send_reqeust(request)

        self.logger.info("delete_dns_record end: %s", (domain_name, domain_dns_value, resp.json()))
        return resp
