import os
import posixpath
# Python 2/3 compatibility
try:
    from urlparse import urlparse
except ImportError:
    from urllib.parse import urlparse
from parallels.core.utils.application_utils import get_application_model
from parallels.core.utils.common import cached, safe_format
from parallels.core.utils.file_utils import get_relative_path
from parallels.core.utils.migrator_utils import normalize_domain_name
from parallels.plesk.source.web import messages
from parallels.plesk.source.web.migrated_database import WebMigratedDatabase
from parallels.plesk.source.web.migrated_folder import MigratedFolder


class MigratedApplication(object):
    """
    :type _global_context: parallels.plesk.source.web.global_context.WebGlobalMigrationContext
    :type _migration_list_application: parallels.plesk.source.web.site_migration_list.MigrationListApplication
    """

    HTTP = 'http'
    HTTPS = 'https'

    def __init__(self, global_context, migration_list_application):
        """
        :type global_context: parallels.plesk.source.web.global_context.WebGlobalMigrationContext
        :type migration_list_application: parallels.plesk.source.web.site_migration_list.MigrationListApplication
        """
        self._global_context = global_context
        self._migration_list_application = migration_list_application

    def description(self):
        """
        :rtype: str | unicode
        """
        return safe_format(
            messages.APPLICATION_DESCRIPTION, application=self.application_model.name,
            source_path=self.source_path, target_path=self.target_path
        )

    @property
    @cached
    def application_model(self):
        """
        :rtype: parallels.core.application.base.ApplicationModel | None
        """
        return get_application_model(self._migration_list_application.application_type)

    @property
    @cached
    def source_instance(self):
        """Source application instance

        :rtype: parallels.core.application.base.ApplicationInstance
        """
        if self.application_model is None:
            return None
        return self.application_model.get_instance(self.source_server, self.source_path)

    @property
    @cached
    def target_instance(self):
        """Target application instance

        :rtype: parallels.core.application.base.ApplicationInstance
        """
        if self.application_model is None:
            return None
        return self.application_model.get_instance(self.target_server, self.target_path)

    @property
    @cached
    def source_path(self):
        """Path where the application is installed on the source server

        :rtype: str | unicode
        """
        return self._migration_list_application.source_path

    @property
    def target_relative_path(self):
        """
        :rtype: str | unicode
        """
        return self._migration_list_application.target_path

    @property
    @cached
    def target_path(self):
        """Path where the application is installed on the target server

        :rtype: str | unicode
        """
        target_document_root = self._global_context.site_info_storage.get_target_document_root()
        return os.path.join(target_document_root, self._migration_list_application.target_path)

    @property
    @cached
    def source_server(self):
        """Source server of the application

        :rtype: parallels.plesk.source.web.connections.WebSource
        """
        return self._global_context.conn.get_source()

    @property
    @cached
    def target_server(self):
        """Target server of the application

        :rtype: parallels.plesk.connections.target_server.PleskTargetServer
        """
        return self._global_context.conn.target.plesk_server

    @property
    @cached
    def databases(self):
        """List of migrated databases of the application

        :rtype: list[parallels.plesk.source.web.migrated_database.WebMigratedDatabase]
        """
        databases = []

        for source_database in self.source_instance.get_databases():
            migrated_database = WebMigratedDatabase(
                self._global_context, self._subscription,
                source_database.name, source_database.host, source_database.port,
                source_database.username, source_database.password, source_database.prefix
            )
            if migrated_database.is_valid():
                databases.append(migrated_database)

        return databases

    @property
    def invalid_databases(self):
        """List of migrated databases of the application with invalid parameters

        :rtype: list[parallels.plesk.source.web.migrated_database.WebMigratedDatabase]
        """
        databases = []

        for source_database in self.source_instance.get_databases():
            migrated_database = WebMigratedDatabase(
                self._global_context, self._subscription,
                source_database.name, source_database.host, source_database.port,
                source_database.username, source_database.password, source_database.prefix
            )
            if not migrated_database.is_valid():
                databases.append(migrated_database)

        return databases

    @property
    @cached
    def domain_changed(self):
        """Check if domains of application on source and target are different
        dom1.tld -> dom2.tld - True
        dom1.tld -> dom1.rld - False

        :rtype: bool
        """
        # Source web site hostname
        source_netloc = self._global_context.conn.get_source().config.host

        # Target web site hostname
        target_netloc = self._global_context.conn.get_source().config.target_domain

        return normalize_domain_name(source_netloc) != normalize_domain_name(target_netloc)

    @property
    @cached
    def url_changed(self):
        """Check if URLs of application on source and target are different
        dom1.tld/app -> dom1.tld/myapp - True
        dom1.tld/app -> dom2.tld/app - False

        :rtype: bool
        """
        return self.source_relative_path != self.target_relative_path

    @property
    @cached
    def source_relative_path(self):
        """For URL http://site.com/forum return /forum

        :rtype: str | unicode
        """
        # Absolute path of application on source server
        # Looks like /var/www/vhosts/testapps.tld/httpdocs/prestashop
        source_abs_path = self.source_path
        # Absolute path to filesystem (FTP/SSH) root on source
        # Looks like /var/www/vhosts/testapps.tld
        absolute_filesystem_root_path = self._global_context.site_info_storage.get_filesystem_root_path()
        # Source document root relative to filesystem (FTP/SSH) root
        # Looks like /httpdocs
        source_docroot = self._global_context.site_info_storage.get_document_root()
        # Absolute path to web site root on source server
        # Looks like /var/www/vhosts/testapps.tld/httpdocs
        site_root = posixpath.join(absolute_filesystem_root_path, source_docroot.lstrip('/'))

        # Source base URL (without hostname)
        return get_relative_path(
            source_abs_path, site_root, False,
            self._global_context.site_info_storage.get_is_windows(),
            self._global_context.site_info_storage.get_is_windows()
        )

    @property
    @cached
    def source_domain(self):
        """
        :rtype: str | unicode
        """
        return self._global_context.conn.get_source().config.host

    @property
    @cached
    def target_domain(self):
        """
        :rtype: str | unicode
        """
        return self._global_context.site_info_storage.get_target_domain_name()

    @property
    @cached
    def target_domain_display_name(self):
        """
        :rtype: str | unicode
        """
        return self._global_context.site_info_storage.get_target_domain_display_name()

    @property
    @cached
    def source_protocol(self):
        """
        :rtype: str | unicode
        """
        src_url = self._global_context.site_info_storage.get_base_url()
        return urlparse(src_url)[0]

    @property
    @cached
    def target_protocol(self):
        """This method should be improved and get available protocols from Plesk
        :rtype: str | unicode
        """
        if self._global_context.site_info_storage.get_target_hosting_ssl():
            return self.HTTPS
        return self.HTTP

    @property
    @cached
    def path_excludes(self):
        """List of migrated folders to be skipped from copying

        :rtype: list[str | unicode]
        """
        excludes = []

        source_relative_path = get_relative_path(
            self.source_path,
            self.source_server.get_absolute_filesystem_root_path(),
            False,
            self.source_server.is_windows(),
            self.source_server.is_windows()
        )

        for exclude in self.source_instance.get_path_excludes(self.domain_changed, self.url_changed):
            excludes.append(
                posixpath.join('/', source_relative_path, exclude.lstrip('/'))
            )

        return excludes

    @property
    @cached
    def folders(self):
        """List of migrated folders of the application

        :rtype: list[parallels.plesk.source.web.migrated_folder.MigratedFolder]
        """
        return [
            MigratedFolder(
                self._global_context, self._subscription,
                self._migration_list_application.source_path,
                self._migration_list_application.target_path,
                self.path_excludes
            )
        ]

    @property
    @cached
    def _subscription(self):
        """
        :rtype: parallels.core.migrated_subscription.MigratedSubscription
        """
        subscription_name = self._global_context.site_info_storage.get_target_subscription()
        return self._global_context.get_subscription(subscription_name)
