from parallels.core import get_logger, safe_format
from parallels.core.reports.model.issue import Issue
from parallels.core.utils.common.ip import is_ipv4
from parallels.plesk.source.web import messages
from parallels.plesk.source.web.actions.base.application import ApplicationAction
from parallels.plesk.source.web.utils.check_utils import check_site
from parallels.plesk.source.web.utils.status_storage import StatusStorage

logger = get_logger(__name__)


class CheckApps(ApplicationAction):
    """Check migrated apps comparing their pages
    """

    def get_description(self):
        """Get short description of action as string

        :rtype: str | unicode
        """
        return messages.ACTION_CHECK_APPS_DESCRIPTION

    def get_failure_message(self, global_context):
        """Get message for situation when action failed

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :rtype: str | unicode
        """
        return messages.ACTION_CHECK_APPS_FAILURE

    def get_failure_message_for_application(self, global_context, application):
        """Get message for situation when action failed for specific application

        :type global_context: parallels.plesk.source.web.global_context.WebGlobalMigrationContext
        :type application: parallels.plesk.source.web.migrated_application.MigratedApplication
        :rtype: str | unicode | None
        """
        return safe_format(
            messages.ACTION_CHECK_APPS_FAILURE_FOR_APP,
            application_type=application.application_model.name,
            path=application.target_path
        )

    def get_failure_severity_for_application(self):
        """Get issue severity for application in case if that action failed

        :rtype: str | unicode
        """
        return Issue.SEVERITY_WARNING

    def get_storage_status_type(self):
        """Category for status in status storage

        :rtype: str | unicode
        """
        return StatusStorage.STORAGE_STATUS_TYPE_CHECK_STATUS

    def get_storage_issue_type(self):
        """Category for issues in status storage

        :rtype: str | unicode
        """
        return StatusStorage.STORAGE_ISSUES_TYPE_CHECK_ISSUES

    def run_for_application(self, global_context, application):
        """Run action

        :type global_context: parallels.plesk.source.web.global_context.WebGlobalMigrationContext
        :type application: parallels.plesk.source.web.migrated_application.MigratedApplication
        :rtype: list[parallels.core.reports.model.issue.Issue] | None
        """
        # If source website IP address will not be defined, it will be resolved.
        # No need to define source website IP address explicitly, because it is required for Site Import
        # that source website is resolvable from target server.
        source_ip = None

        target_ip = self._choose_target_ip_address(global_context.site_info_storage.get_target_ip_addresses())

        if application.source_relative_path is None:
            return [Issue('check-impossible', Issue.SEVERITY_WARNING, messages.CHECK_IMPOSSIBLE_NO_SOURCE_URL)]

        issues = check_site(
            target_domain=application.target_domain_display_name,
            target_ip=target_ip,
            target_protocol=application.target_protocol,
            target_relative_url=application.target_relative_path.lstrip('/'),
            source_domain=application.source_domain,
            source_ip=source_ip,
            source_protocol=application.source_protocol,
            source_relative_url=application.source_relative_path.lstrip('/'),
            messages=self._get_messages()
        )

        if len(issues) == 0:
            issues = [Issue('check-finished', Issue.SEVERITY_INFO, messages.CHECK_FINISHED_NO_ISSUES)]

        return issues

    @staticmethod
    def _choose_target_ip_address(ip_addresses):
        """Domain can have multiple IP addresses. For example ipv4 and ipv6.
        Here we choose one.

        :type ip_addresses: list[str | unicode]
        :rtype: str | unicode | None
        """
        if len(ip_addresses) == 0:
            raise Exception(messages.FAILED_TO_GET_TARGET_WEBSITE_IP_ADDRESS)

        for ip_address in ip_addresses:
            if is_ipv4(ip_address):
                return ip_address

        return ip_addresses[0]

    @staticmethod
    def _get_messages():
        return dict(
            web_availability_signature_found_on_source=messages.SITE_CHECK_WEB_AVAILABILITY_SIGNATURE_FOUND_ON_SOURCE,
            web_availability_webserver_returned_http_code_both=(
                messages.SITE_CHECK_WEB_AVAILABILITY_WEBSERVER_RETURNED_HTTP_CODE_BOTH
            ),
            web_availability_different_http_code=messages.SITE_CHECK_WEB_AVAILABILITY_DIFFERENT_HTTP_CODE,
            web_availability_title_changed=messages.SITE_CHECK_WEB_AVAILABILITY_TITLE_CHANGED
        )
