import os.path
from parallels.core import MigrationNoRepeatError

from parallels.core import messages
from parallels.core.migrator_config import read_ssh_auth, \
    read_windows_auth, read_windows_agent_settings, \
    read_copy_mail_content_settings, get_local_session_dir, SourceUnixServerConfig, SourceWindowsServerConfig
from parallels.core.utils.common_constants import PLESK_PANEL_DEFAULT_PORT, WINDOWS_SOURCE_DEFAULT_SESSION_DIR, \
    UNIX_SOURCE_DEFAULT_SESSION_DIR, WINDOWS_REMOTE_TARGET_DEFAULT_SESSION_DIR
from parallels.core.utils.config_utils import ConfigSection, read_http_connection_settings
from parallels.core.utils.common import default, is_empty


class PleskConfig(object):
    def __init__(self, is_skip_extensions=False, is_migrate_server_wide_extensions=False):
        """
        :type is_skip_extensions: bool
        :type is_migrate_server_wide_extensions: bool
        """
        self._is_skip_extensions = is_skip_extensions
        self._is_migrate_server_wide_extensions = is_migrate_server_wide_extensions

    @property
    def is_skip_extensions(self):
        """
        :rtype: bool
        """
        return self._is_skip_extensions

    @property
    def is_migrate_server_wide_extensions(self):
        """
        :rtype: bool
        """
        return self._is_migrate_server_wide_extensions


class TargetPleskConfigBase(PleskConfig):
    def __init__(
        self, ip, is_local, plesk_api, session_dir, is_skip_extensions,
        cligate_ip=None, cligate_host=None, apache_restart_interval=None,
        is_migrate_server_wide_extensions=False, is_skip_external_id=False
    ):
        super(TargetPleskConfigBase, self).__init__(is_skip_extensions, is_migrate_server_wide_extensions)
        self._ip = ip
        self._is_local = is_local
        self._cligate_ip = cligate_ip
        self._cligate_host = cligate_host
        self._plesk_api = plesk_api
        self._session_dir = session_dir
        self._apache_restart_interval = apache_restart_interval
        self._is_skip_external_id = is_skip_external_id

    @property
    def ip(self):
        return self._ip

    @property
    def cligate_host(self):
        default_cligate_host = 'localhost' if self.is_local else None
        cligate_host = default(self._cligate_host, default_cligate_host)
        if is_empty(cligate_host):
            cligate_host = None
        return cligate_host

    @property
    def cligate_ip(self):
        default_cligate_ip = '127.0.0.1' if self.is_local else self.ip
        return default(self._cligate_ip, default_cligate_ip)

    @property
    def plesk_api(self):
        return self._plesk_api

    @property
    def session_dir(self):
        return self._session_dir

    @property
    def apache_restart_interval(self):
        return self._apache_restart_interval

    @property
    def is_windows(self):
        raise NotImplementedError()

    @property
    def is_local(self):
        """If target server is local or remote

        Local server means that migrator works on the target node, remote means
        that migrator's node and target node are different servers
        :rtype: bool
        """
        return self._is_local

    @property
    def is_skip_external_id(self):
        return self._is_skip_external_id


class TargetPleskUnixConfig(TargetPleskConfigBase):
    def __init__(
        self, ip, is_local, ssh_auth, plesk_api, session_dir, is_skip_extensions,
        cligate_ip=None, cligate_host=None, apache_restart_interval=None,
        is_migrate_server_wide_extensions=False, is_skip_external_id=False
    ):
        super(TargetPleskUnixConfig, self).__init__(
            ip, is_local, plesk_api, session_dir, is_skip_extensions, cligate_ip, cligate_host,
            apache_restart_interval,
            is_migrate_server_wide_extensions=is_migrate_server_wide_extensions,
            is_skip_external_id=is_skip_external_id
        )
        self._ssh_auth = ssh_auth

    @property
    def ssh_auth(self):
        return self._ssh_auth

    @property
    def is_windows(self):
        return False


class TargetPleskWindowsConfig(TargetPleskConfigBase):
    def __init__(
        self, ip, is_local, windows_auth, plesk_api, session_dir, is_skip_extensions,
        agent_settings, cligate_ip=None, cligate_host=None, remote_mssql_session_dir=None,
        is_migrate_server_wide_extensions=False, is_skip_external_id=False
    ):
        super(TargetPleskWindowsConfig, self).__init__(
            ip, is_local, plesk_api, session_dir, is_skip_extensions, cligate_ip, cligate_host,
            # Apache restart interval is not applicable for Windows
            apache_restart_interval=None,
            is_migrate_server_wide_extensions=is_migrate_server_wide_extensions,
            is_skip_external_id=is_skip_external_id
        )
        self._windows_auth = windows_auth
        self._agent_settings = agent_settings
        self._remote_mssql_session_dir = remote_mssql_session_dir

    @property
    def windows_auth(self):
        return self._windows_auth

    @property
    def agent_settings(self):
        return self._agent_settings

    @property
    def is_windows(self):
        return True

    @property
    def remote_mssql_session_dir(self):
        """A directory for temporary files on target remote MSSQL servers

        :rtype: str | unicode
        """
        return self._remote_mssql_session_dir


class SourcePleskUnixConfig(PleskConfig, SourceUnixServerConfig):
    def __init__(
        self, config_section, server_id, ip, session_dir, is_skip_extensions,
        ssh_auth, mail_settings, plesk_api, ssh_authorized_keys_file=None
    ):
        PleskConfig.__init__(self, is_skip_extensions)
        SourceUnixServerConfig.__init__(
            self, config_section, server_id, ip, session_dir, ssh_auth, mail_settings
        )
        self._plesk_api = plesk_api
        self._ssh_authorized_keys_file = ssh_authorized_keys_file

    @property
    def plesk_api(self):
        return self._plesk_api

    @property
    def ssh_authorized_keys_file(self):
        return self._ssh_authorized_keys_file


class SourcePleskWindowsConfig(PleskConfig, SourceWindowsServerConfig):
    def __init__(
            self, config_section, server_id, ip, session_dir, is_skip_extensions,
            windows_auth, agent_settings, mail_settings, plesk_api, remote_mssql_session_dir=None,
    ):
        PleskConfig.__init__(self, is_skip_extensions)
        SourceWindowsServerConfig.__init__(
            self, config_section, server_id, ip, session_dir, windows_auth, agent_settings, mail_settings,
            remote_mssql_session_dir
        )
        self._plesk_api = plesk_api

    @property
    def plesk_api(self):
        return self._plesk_api


def read_source_dns_servers_settings(config, section_name):
    """Read centralized dns ip addresses and credentials.
    """
    section = ConfigSection(config, section_name)

    ip = section['ip']
    ssh_auth = read_ssh_auth(section)
    return SourcePleskUnixConfig(section, section_name, ip, None, True, ssh_auth, None, None)


def read_target_plesk_settings(config, section_name):
    """
    :rtype:
        parallels.plesk.config.TargetPleskWindowsConfig |
        parallels.plesk.config.TargetPleskUnixConfig
    """
    section = ConfigSection(config, section_name)
    ip = section['ip']
    target_os = section['os']
    is_local = section.getboolean('is-local', True)
    cligate_ip = section.get('cligate-ip')
    cligate_host = section.get('cligate-host')
    plesk_settings = dict(
        ip=ip,
        host='localhost',
        username=section.get('panel-username', 'admin'),
        password=section.get_password('panel-password'),
    )
    plesk_api = read_http_connection_settings(
        section.prefixed('plesk-').with_defaults(dict(PLESK_URL_DEFAULTS, **plesk_settings))
    )
    if is_local:
        local_session_dir = get_local_session_dir(config)
        default_session_dir = os.path.join(local_session_dir, 'target-server')
    else:
        if target_os == 'windows':
            default_session_dir = WINDOWS_REMOTE_TARGET_DEFAULT_SESSION_DIR
        else:
            default_session_dir = UNIX_SOURCE_DEFAULT_SESSION_DIR
    session_dir = section.get('session-dir', default_session_dir)
    is_skip_extensions = section.getboolean('skip-extensions', False)
    is_skip_external_id = section.getboolean('skip-external-id', False)
    is_migrate_server_wide_extensions = section.getboolean('migrate-server-wide-extensions-data', False)

    if target_os == 'windows':
        windows_auth = read_windows_auth(section, required=False)
        remote_mssql_session_dir = section.get('remote-mssql-session-dir', WINDOWS_REMOTE_TARGET_DEFAULT_SESSION_DIR)
        return TargetPleskWindowsConfig(
            ip=ip, is_local=is_local, windows_auth=windows_auth, plesk_api=plesk_api,
            session_dir=session_dir, is_skip_extensions=is_skip_extensions,
            agent_settings=read_windows_agent_settings(config, section_name, session_dir),
            cligate_ip=cligate_ip, cligate_host=cligate_host,
            remote_mssql_session_dir=remote_mssql_session_dir,
            is_migrate_server_wide_extensions=is_migrate_server_wide_extensions,
            is_skip_external_id=is_skip_external_id
        )
    else:
        ssh_auth = read_ssh_auth(section, required=False)
        if not is_local and ssh_auth is None:
            raise MigrationNoRepeatError(messages.SSH_AUTH_NOT_SPECIFIED.format(section_name=section_name))
        apache_restart_interval = section.get('apache-restart-interval', '1800')
        return TargetPleskUnixConfig(
            ip=ip, is_local=is_local, ssh_auth=ssh_auth, plesk_api=plesk_api,
            session_dir=session_dir, is_skip_extensions=is_skip_extensions,
            cligate_ip=cligate_ip, cligate_host=cligate_host,
            apache_restart_interval=apache_restart_interval,
            is_migrate_server_wide_extensions=is_migrate_server_wide_extensions,
            is_skip_external_id=is_skip_external_id
        )


def read_source_plesk_settings(config, section_name):
    """Read Plesk API and SSH connection settings.
    """
    section = ConfigSection(config, section_name)

    ip = section['ip']
    os = section['os']
    session_dir = section.get(
        'session-dir',
        WINDOWS_SOURCE_DEFAULT_SESSION_DIR if os == 'windows' else UNIX_SOURCE_DEFAULT_SESSION_DIR
    )
    is_skip_extensions = section.getboolean('skip-extensions', False)
    is_windows = (os == 'windows')
    mail_settings = read_copy_mail_content_settings(section, is_windows)
    plesk_api = _read_plesk_connection_settings(config, section_name)

    if os == 'windows':
        windows_agent_settings = read_windows_agent_settings(config, section_name, session_dir)
        windows_auth = read_windows_auth(section, required=not windows_agent_settings.skip_agent_deployment)
        remote_mssql_session_dir = section.get('remote-mssql-session-dir', session_dir)
        return SourcePleskWindowsConfig(
            section, section_name, ip, session_dir, is_skip_extensions, windows_auth,
            windows_agent_settings, mail_settings, plesk_api, remote_mssql_session_dir
        )
    else:
        ssh_auth = read_ssh_auth(section)
        ssh_authorized_keys_file = section.get('ssh-authorized-keys-file')
        return SourcePleskUnixConfig(
            section, section_name, ip, session_dir, is_skip_extensions,
            ssh_auth, mail_settings, plesk_api, ssh_authorized_keys_file
        )


PLESK_URL_DEFAULTS = dict(
    protocol='https',
    port=PLESK_PANEL_DEFAULT_PORT,
    path='/enterprise/control/agent.php'
)


def _read_plesk_connection_settings(config, section_name):
    """Read Plesk API connection settings.
    """
    section = ConfigSection(config, section_name)

    api_defaults = dict(
        PLESK_URL_DEFAULTS,
        ip=section.get('ip'),
        host='localhost',
        username=section.get('panel-username', 'admin'),
        password=section.get_password('panel-password'),
    )
    return read_http_connection_settings(
        section.prefixed('plesk-').with_defaults(api_defaults)
    )

