import posixpath

from parallels.core import MigrationError
from parallels.core import safe_format
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.logging import get_logger
from parallels.core.runners.base import BaseRunner
from parallels.core.utils import subscription_filter
from parallels.core.utils.entity import Entity
from parallels.core.utils.plesk_utils import convert_wildcard_domain_path
from parallels.plesk.source.plesk import messages

logger = get_logger(__name__)


class Link(Entity):
    def __init__(self, source, link, is_hardlink):
        self._source = source
        self._link = link
        self._is_hardlink = is_hardlink

    @property
    def source(self):
        return self._source

    @property
    def link(self):
        return self._link

    @property
    def is_hardlink(self):
        return self._is_hardlink


class FixUnixVhostStructure(SubscriptionAction):
    """Base class to fix vhost structure for Unix servers"""

    def get_description(self):
        return messages.ACTION_FIX_UNIX_VHOST_STRUCTURE_DESCRIPTION

    def get_failure_message(self, global_context, subscription):
        return messages.ACTION_FIX_UNIX_VHOST_STRUCTURE_FAILURE % subscription.name

    def is_critical(self):
        """If action is critical or not

        If action is critical and it failed for a subscription, migration tool
        won't run the next operations for the subscription.

        :rtype: bool
        """
        return False

    def filter_subscription(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return subscription_filter.unix_with_virtual_hosting(
            subscription
        )

    def run(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """

        target_server = subscription.web_target_server

        links = self._get_links(subscription, target_server.vhosts_dir)
        with target_server.runner() as runner_target:
            assert isinstance(runner_target, BaseRunner)
            try:
                for link in links:
                    if link.is_hardlink:
                        for source_file in runner_target.get_files_list(link.source):
                            if runner_target.is_dir(posixpath.join(link.source, source_file)):
                                logger.fdebug(messages.SKIP_RELINKING_DIR, directory=source_file)
                                continue

                            runner_target.execute_command(
                                'ln -f {source}/{source_file} {link}',
                                dict(
                                    source=link.source,
                                    source_file=source_file,
                                    link=link.link
                                )
                            )
                    else:
                        runner_target.execute_command(
                            'ln -s -f {source} {link}',
                            dict(
                                source=link.source,
                                link=link.link
                            )
                        )
            except Exception as e:
                logger.exception()
                raise MigrationError(
                    safe_format(
                        messages.CAN_NOT_CREATE_LINKS_FOR_SUBSCRIPTION,
                        subscription=subscription.name_idn,
                        reason=e
                    )
                )

    def _get_links(self, subscription, vhosts_dir):
        """
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        :type vhosts_dir: str | unicode
        :rtype: list[parallels.plesk.source.plesk.actions.content.web.fix_unix_vhost_structure.Link]
        """

        vhost_name = subscription.name_idn
        if subscription.web_target_server.get_plesk_version()[:2] >= (17, 9):
            vhost_name = vhost_name.lower()

        links = [
            Link(
                posixpath.join(vhosts_dir, 'system', vhost_name, 'logs'),
                posixpath.join(vhosts_dir, vhost_name, 'logs'),
                True
            ),
            Link(
                posixpath.join(vhosts_dir, 'system', vhost_name, 'logs'),
                posixpath.join(vhosts_dir, 'system', vhost_name, 'statistics'),
                False
            ),
        ]

        source_vhosts_dir = subscription.web_source_server.vhosts_dir
        with subscription.web_source_server.runner() as runner:
            if runner.file_exists(posixpath.join(source_vhosts_dir, vhost_name, 'statistics')):
                links.append(Link(
                    posixpath.join(vhosts_dir, 'system', vhost_name, 'statistics'),
                    posixpath.join(vhosts_dir, vhost_name),
                    False
                ))
            if runner.file_exists(posixpath.join(source_vhosts_dir, vhost_name, 'conf')):
                links.append(
                    Link(
                        posixpath.join(vhosts_dir, 'system', vhost_name, 'conf'),
                        posixpath.join(vhosts_dir, vhost_name), False
                    )
                )
        
        for site in subscription.converted_dump.iter_sites():
            if site.is_virtual_hosting:
                site_name = convert_wildcard_domain_path(site.name.encode('idna'))
                if subscription.web_target_server.get_plesk_version()[:2] >= (17, 9):
                    site_name = site_name.lower()
                links.extend([
                    Link(
                        posixpath.join(vhosts_dir, 'system', site_name, 'logs'),
                        posixpath.join(vhosts_dir, vhost_name, 'logs', site_name),
                        True
                    ),
                    Link(
                        posixpath.join(vhosts_dir, 'system', site_name, 'logs'),
                        posixpath.join(vhosts_dir, 'system', site_name, 'statistics'),
                        False
                    ),
                ])

        return links
