from parallels.core import MigrationError
from parallels.core.hosting_repository.dns_record import DnsRecordModel, DnsRecordEntity
from parallels.core.utils.migrator_utils import safe_idn_decode
from parallels.core.utils.mysql import escape_args_list
from parallels.plesk import messages
from parallels.plesk.hosting_repository.base import PleskBaseModel
from parallels.plesk.hosting_repository.utils.cli.dns import DnsRecordNsRemoveCli, DnsRecordARemoveCli, \
    DnsRecordAaaaRemoveCli, DnsRecordMxRemoveCli, DnsRecordTxtRemoveCli, DnsRecordDsRemoveCli, DnsRecordCnameRemoveCli


class PleskDnsRecordModel(DnsRecordModel, PleskBaseModel):
    def get_list(self, filter_domain_name=None, filter_type=None, filter_name=None):
        """Retrieve list of DNS records

        :type filter_domain_name: list[str] | None
        :type filter_type: list[str] | None
        :type filter_name: list[str] | None
        :rtype: list[parallels.core.hosting_repository.dns_record.DnsRecordEntity]
        """
        if filter_domain_name is not None and len(filter_domain_name) == 0:
            #  filter by domain name is empty, so no one DNS record could be retrieved
            return []

        if filter_type is not None and len(filter_type) == 0:
            #  filter by type is empty, so no one DNS record could be retrieved
            return []

        if filter_name is not None and len(filter_name) == 0:
            #  filter by name is empty, so no one DNS record could be retrieved
            return []

        dns_records = []

        query = """
            SELECT
                domains.name as domain_name,
                dns_recs.type as type,
                dns_recs.host as name,
                dns_recs.val as data
            FROM
                dns_recs JOIN domains on dns_recs.dns_zone_id = domains.dns_zone_id
            WHERE
                1
        """
        query_args = {}
        if filter_domain_name is not None:
            filter_domain_name_placeholders, filter_domain_name_values = escape_args_list(
                filter_domain_name, 'domain_name'
            )
            query += ' AND domains.name in ({filter_domain_name_placeholders_str})'.format(
                filter_domain_name_placeholders_str=', '.join(filter_domain_name_placeholders)
            )
            # idn domains stored in database in idna encoding, so perform encoding of given filter
            query_args.update({key: value.encode('idna') for key, value in filter_domain_name_values.items()})

        if filter_type is not None:
            filter_type_placeholders, filter_type_values = escape_args_list(filter_type, 'type')
            query += ' AND dns_recs.type in ({filter_type_placeholders_str})'.format(
                filter_type_placeholders_str=', '.join(filter_type_placeholders)
            )
            query_args.update(filter_type_values)

        if filter_name is not None:
            filter_name_placeholders, filter_name_values = escape_args_list(filter_name, 'name')
            query += ' AND dns_recs.host in ({filter_name_placeholders_str})'.format(
                filter_name_placeholders_str=', '.join(filter_name_placeholders)
            )
            # DNS records of idn domains stored in database in idna encoding, so perform encoding of given filter
            query_args.update({key: value.encode('idna') for key, value in filter_name_values.items()})

        for row in self.plesk_server.query_panel_db(query, query_args):
            dns_records.append(DnsRecordEntity(
                record_type=row['type'],
                record_name=safe_idn_decode(row['name']),
                record_data=safe_idn_decode(row['data']),
                domain_name=safe_idn_decode(row['domain_name'])
            ))

        return dns_records

    def remove(self, dns_record):
        """Remove subscription from the server

        :type dns_record: parallels.core.hosting_repository.dns_record.DnsRecordEntity
        """
        suffix = '.%s.' % dns_record.domain_name
        if not dns_record.name.endswith(suffix):
            raise MigrationError(messages.HOSTING_REPOSITORY_DNS_RECORD_REMOVE_ERROR_NAME.format(
                dns_record_pretty_name=dns_record.pretty_name,
                domain_name=dns_record.domain_name,
                source_description=self.plesk_server.description()
            ))
        relative_name = dns_record.name[0:-len(suffix)]
        if dns_record.type == DnsRecordEntity.TYPE_NS:
            DnsRecordNsRemoveCli(self.plesk_cli_runner, dns_record.domain_name, relative_name, dns_record.data).run()
        elif dns_record.type == DnsRecordEntity.TYPE_A:
            DnsRecordARemoveCli(self.plesk_cli_runner, dns_record.domain_name, relative_name, dns_record.data).run()
        elif dns_record.type == DnsRecordEntity.TYPE_AAAA:
            DnsRecordAaaaRemoveCli(
                self.plesk_cli_runner, dns_record.domain_name, relative_name, dns_record.data
            ).run()
        elif dns_record.type == DnsRecordEntity.TYPE_CNAME:
            DnsRecordCnameRemoveCli(
                self.plesk_cli_runner, dns_record.domain_name, relative_name, dns_record.data
            ).run()
        elif dns_record.type == DnsRecordEntity.TYPE_MX:
            DnsRecordMxRemoveCli(self.plesk_cli_runner, dns_record.domain_name, relative_name, dns_record.data).run()
        elif dns_record.type == DnsRecordEntity.TYPE_TXT:
            DnsRecordTxtRemoveCli(self.plesk_cli_runner, dns_record.domain_name, relative_name, dns_record.data).run()
        elif dns_record.type == DnsRecordEntity.TYPE_DS:
            DnsRecordDsRemoveCli(self.plesk_cli_runner, dns_record.domain_name, relative_name, dns_record.data).run()
        else:
            raise MigrationError(messages.HOSTING_REPOSITORY_DNS_RECORD_REMOVE_ERROR_UNSUPPORTED_TYPE.format(
                dns_record_type=dns_record.type
            ))
