import sys
from xml.etree import ElementTree

from parallels.core.logging import get_logger
from parallels.core.utils.common import open_no_inherit
from parallels.core.utils.common.xml import elem, xml_to_string_pretty, seq, seq_iter
from parallels.plesk.source.plesk import messages
from parallels.plesk.source.plesk.shallow_dump.model.base.template import Template

logger = get_logger(__name__)


class ShallowXMLConverter(object):
    """Class to create shallow dump XML out of shallow model"""

    def __init__(self, shallow_model):
        """
        :type shallow_model: parallels.plesk.source.plesk.shallow_dump.model.base.model.ShallowDumpModel
        """
        self._shallow_model = shallow_model

    def write_xml(self, filename):
        shallow_dump_contents = xml_to_string_pretty(self.create_xml())
        if sys.version_info.major >= 3:
            with open_no_inherit(filename, 'wb') as fp:
                fp.write(shallow_dump_contents)
        else:
            with open_no_inherit(filename, 'w') as fp:
                fp.write(shallow_dump_contents)

    def create_xml(self):
        """Create shallow dump XML tree out of shallow model

        :rtype: xml.etree.ElementTree.ElementTree
        """
        dump_tree = ElementTree.ElementTree(
            elem(
                'migration-dump',
                [
                    elem('admin', seq(
                        elem('resellers', [
                            self._create_reseller_node(reseller) for reseller in self._shallow_model.resellers
                        ]),
                        elem('clients', [
                            self._create_client_node(client) for client in self._shallow_model.admin_clients
                        ]),
                        elem('domains', [
                            self._create_domain_node(domain) for domain in self._shallow_model.admin_domains
                        ])
                    )),
                    elem('server', [
                        elem('account-templates', self._create_admin_template_nodes())
                    ])
                ],
                {
                    'dump-version': '12.5',
                    'agent-name': 'panel-migrator'
                }
            )
        )

        return dump_tree

    def _create_admin_template_nodes(self):
        """Create XML nodes for admin's domain and reseller templates

        :rtype: list[xml.etree.ElementTree.Element]
        """
        return (
            [
                self._create_domain_template_node(template)
                for template in self._shallow_model.admin_domain_templates
            ] +
            [
                self._create_reseller_template_node(template)
                for template in self._shallow_model.admin_reseller_templates
            ]
        )

    def _create_subscription_plan_node(self, plan_id):
        """Create plan node that should be put inside 'subscription' node

        :type plan_id: str | unicode
        :rtype: xml.etree.ElementTree.Element
        """
        if plan_id not in self._shallow_model.templates_by_id:
            logger.fwarn(messages.SOURCE_PLESK_INVALID_PLAN_ID, plan_id=plan_id)
            return None

        template = self._shallow_model.templates_by_id[plan_id]
        attributes = {'plan-guid': template.guid}
        if template.template_type == Template.TEMPLATE_TYPE_DOMAIN_ADDON:
            attributes['is-addon'] = 'true'
        return elem('plan', [], attributes)

    def _create_domain_node(self, domain):
        """Create XML node for domain

        :type domain: parallels.plesk.source.plesk.shallow_dump.model.base.domain.Domain
        :rtype: xml.etree.ElementTree.Element
        """
        preferences_child_nodes = []
        if domain.subscription_id is not None:
            subscription_plan_ids = self._shallow_model.subscription_id_to_plan_ids.get(domain.subscription_id, [])
            preferences_child_nodes.append(
                elem(
                    'subscription', seq_iter(
                        self._create_subscription_plan_node(plan_id)
                        for plan_id in subscription_plan_ids
                    )
                )
            )

        return elem(
            'domain',
            [
                elem('preferences', preferences_child_nodes)
            ],
            {'name': domain.name}
        )

    def _create_client_node(self, client):
        """Create XML node for client

        :type client: parallels.plesk.source.plesk.shallow_dump.model.base.client.Client
        :rtype: xml.etree.ElementTree.Element
        """
        return elem(
            'client', [
                elem('domains', [
                    self._create_domain_node(domain)
                    for domain in self._shallow_model.get_client_domains(client.client_id)
                ])
            ],
            {'name': client.login, 'contact': client.contact_name}
        )

    def _create_reseller_node(self, reseller):
        """Create XML node for reseller

        :type reseller: parallels.plesk.source.plesk.shallow_dump.model.base.reseller.Reseller
        :rtype: xml.etree.ElementTree.Element
        """
        subscription_plan_ids = self._shallow_model.subscription_id_to_plan_ids.get(reseller.subscription_id, [])
        return elem(
            'reseller',
            [
                elem('preferences', (
                    [
                        self._create_domain_template_node(template)
                        for template in self._shallow_model.get_reseller_domain_templates(reseller.reseller_id)
                    ] +
                    [elem('subscription', seq_iter(
                        self._create_subscription_plan_node(plan_id)
                        for plan_id in subscription_plan_ids
                    ))]
                )),
                elem('clients', [
                    self._create_client_node(client)
                    for client in self._shallow_model.get_reseller_clients(reseller.reseller_id)
                ]),
                elem('domains', [
                    self._create_domain_node(domain)
                    for domain in self._shallow_model.get_reseller_domains(reseller.reseller_id)
                ])
            ],
            {'name': reseller.login, 'contact': reseller.contact_name}
        )

    @staticmethod
    def _create_domain_template_node(template):
        """Create XML node for domain template

        :type template: parallels.plesk.source.plesk.shallow_dump.model.base.template.Template
        :rtype: xml.etree.ElementTree.Element
        """
        attributes = {'name': template.name}
        if template.guid is not None:
            attributes['guid'] = template.guid
        if template.template_type == Template.TEMPLATE_TYPE_DOMAIN_ADDON:
            attributes['is-addon'] = 'true'

        return elem('domain-template', [], attributes)

    @staticmethod
    def _create_reseller_template_node(template):
        """Create XML node for reseller template

        :type template: parallels.plesk.source.plesk.shallow_dump.model.base.template.Template
        :rtype: xml.etree.ElementTree.Element
        """
        attributes = {'name': template.name}
        if template.guid is not None:
            attributes['guid'] = template.guid

        return elem('reseller-template', [], attributes)
