from parallels.core import safe_utf8_encode
from parallels.core.application.base import ApplicationModel, SingleConfigApplicationInstance
from parallels.core.application.utils import replace_line_bounds, replace_database_host_line, get_database
from parallels.core.logging import get_logger
from parallels.core.utils.common import cached
from parallels.core.utils.file_utils import get_nested_dir_names, get_nested_dir_trees
from parallels.core.utils.message_utils import multi_line_message
from parallels.plesk.source.web import messages

logger = get_logger(__name__)


class WordpressModel(ApplicationModel):
    @property
    def id(self):
        return 'wordpress'

    @property
    def name(self):
        return 'WordPress'

    def get_instance(self, server, base_path):
        """Retrieve instance of WordPress

        :type server: parallels.core.connections.server.Server
        :type base_path: str
        :rtype: parallels.core.application.wordpress.WordpressInstance
        """
        return WordpressInstance(server, safe_utf8_encode(base_path))

    def get_supported_database_types(self):
        return [self.DATABASE_TYPE_MYSQL]

    def _search_file(self, server, file_path, file_name):
        return None

    def _search_directory(self, server, directory_path, directory_name, directory_tree):
        config_file_path = None
        for item in directory_tree:
            if item == 'wp-config.php':
                config_file_path = server.join_file_path(directory_path, item)
                file_contents = self._safe_get_file_contents(server, config_file_path)
                if file_contents is None:
                    # failed to read the file (for example it is too large), no need to continue
                    return None

                break
        if config_file_path is None:
            # directory does not contain WordPress configuration file, so no need to continue
            return None
        # configuration file found so check required WordPress directories which should be placed near
        required_directories = ['wp-content', 'wp-includes']
        if all(x in get_nested_dir_names(directory_tree) for x in required_directories):
            # all required directories found, assume that instance of WordPress located
            # in the same directory as configuration file
            return self.get_instance(server, directory_path)

        for nested_directory_name, nested_directory_tree in get_nested_dir_trees(directory_tree):
            if all(x in get_nested_dir_names(nested_directory_tree) for x in required_directories):
                # all required directories found in nested directory,
                # assume that instance of WordPress located there
                base_path = server.join_file_path(directory_path, nested_directory_name)
                return self.get_instance(server, base_path)

        # required WordPress directories not found
        return None


class WordpressInstance(SingleConfigApplicationInstance):
    @property
    def description(self):
        return messages.APPLICATION_DESCRIPTION_WORDPRESS.format(base_path=self.base_path)

    @property
    @cached
    def config_file_path(self):
        with self.server.runner() as runner:
            config_path = self.server.join_file_path(self.base_path, 'wp-config.php')
            if runner.file_exists(config_path):
                return config_path
            config_path = self.server.join_file_path(self.server.get_base_path(self.base_path), 'wp-config.php')
            if runner.file_exists(config_path):
                return config_path
        return None

    def get_paths(self):
        paths = [self.base_path]
        if self.server.get_base_path(self.config_file_path) != self.base_path:
            paths.append(self.config_file_path)
        return paths

    def get_databases(self):
        php_code = multi_line_message("""
        <?php
        include '{config_file_path}';
        echo json_encode(array(
            'host' => DB_HOST,
            'username' => DB_USER,
            'password' => DB_PASSWORD,
            'name' => DB_NAME,
        ));
        """).encode('ascii').format(config_file_path=self.config_file_path)
        return [get_database(php_code, self)]

    def _get_database_hostname_replace_result(self, config_file_contents, database_hostname_mapping):
        """Replace database hostnames in configuration file of application, provided by string

        :type config_file_contents: str | unicode
        :type database_hostname_mapping: dict[str | unicode, str | unicode]
        :rtype: parallels.core.utils.line_processor.ReplaceResults
        """
        # default config has line like:
        # define('DB_HOST', '10.52.143.116');
        return replace_database_host_line(
            config_file_contents, 'DB_HOST', "'DB_HOST', '", "'", database_hostname_mapping
        )

    def _get_database_name_replace_result(self, config_file_contents, database_name_mapping):
        """Replace database names in configuration file of application, provided by string

        :type config_file_contents: str | unicode
        :type database_name_mapping: dict[str | unicode, str | unicode]
        :rtype: parallels.core.utils.line_processor.ReplaceResults
        """
        # default config has line like:
        # define('DB_NAME', 'mywordpress1');
        return replace_line_bounds(
            config_file_contents, ['DB_NAME'], ["""['"]DB_NAME['"]\s*,\s*['"]"""], """['"]""",
            database_name_mapping,
            regex=True
        )

    def _get_database_user_name_replace_result(self, config_file_contents, database_user_name_mapping):
        """Replace database user names in configuration file of application, provided by string

        :type config_file_contents: str | unicode
        :type database_user_name_mapping: dict[str | unicode, str | unicode]
        :rtype: parallels.core.utils.line_processor.ReplaceResults
        """
        # default config has line like:
        # define('DB_USER', 'mywordpress1');
        return replace_line_bounds(
            config_file_contents, ['DB_USER'], ["""['"]DB_USER['"]\s*,\s*['"]"""], """['"]""",
            database_user_name_mapping,
            regex=True
        )
