import re

from parallels.core.actions.utils.logging_properties import LoggingProperties
from parallels.core.application.drupal import DrupalModel
from parallels.core.application.joomla import JoomlaModel
from parallels.core.application.magento import MagentoModel
from parallels.core.application.prestashop import PrestashopModel
from parallels.core.application.utils import ApplicationInstanceDatabase
from parallels.core.logging import get_logger
from parallels.core.reports.model.issue import Issue
from parallels.core.utils.common import safe_format
from parallels.plesk.source.web import messages
from parallels.plesk.source.web.actions.base.application import ApplicationAction

logger = get_logger(__name__)


class AdjustBaseUrlChangeInDB(ApplicationAction):
    """Adjust base URL in application database
    """

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

        :rtype: str | unicode
        """
        return messages.ACTION_ADJUST_BASE_URL_IN_DB_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_ADJUST_BASE_URL_IN_DB_FAILURE

    def get_logging_properties(self):
        """Get how action should be logged to migration tools end-user

        :rtype: parallels.core.actions.utils.logging_properties.LoggingProperties
        """
        return LoggingProperties(compound=False)

    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_for_application(self, global_context, application):
        """Check whether we should run this action for application or not. By default True - action should be executed.

        :type global_context: parallels.plesk.source.web.global_context.WebGlobalMigrationContext
        :type application: parallels.plesk.source.web.migrated_application.MigratedApplication
        :rtype: None
        """
        return application.application_model.name in [
            JoomlaModel().name,
            DrupalModel().name,
            MagentoModel().name,
            PrestashopModel().name
        ]

    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_ADJUST_BASE_URL_IN_DB_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 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
        """
        domain = global_context.conn.get_source().config.target_domain
        relative_path = application.target_relative_path
        issues = []

        for database in application.databases:
            if database.target_prefix is not None and not self._is_valid_db_prefix(database.target_prefix):
                logger.fwarn(
                    messages.DATABASE_ADJUST_FAILED_INVALID_PREFIX,
                    database=database.target_name,
                    prefix=database.target_prefix
                )
                issues.append(Issue(
                    'adjust-base-url-in-database',
                    Issue.SEVERITY_WARNING,
                    safe_format(
                        messages.DATABASE_ADJUST_FAILED_INVALID_PREFIX,
                        database=database.target_name,
                        prefix=database.target_prefix
                    )
                ))
                continue
            target_database = ApplicationInstanceDatabase(
                type='mysql',
                host=database.target_server.host(),
                port=database.target_server.port(),
                socket=None,
                username=database.database_user.target_username,
                password=database.database_user.source_password,
                name=database.target_name,
                prefix=database.target_prefix

            )
            issues.extend(application.target_instance.fix_databases(
                database=target_database,
                domain=domain,
                url=relative_path,
                domain_changed=application.domain_changed,
                url_changed=application.url_changed
            ))
        return issues

    @staticmethod
    def _is_valid_db_prefix(prefix):
        """Verify database prefix for valid symbols

        :type prefix: str | unicode
        :rtype: bool
        """
        return re.match('^[A-Za-z0-9_]*$', prefix)
