# -*- coding: utf-8 -*-

# --------------------------------------------------------------------------
# Copyright Commvault Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# --------------------------------------------------------------------------

"""
File for operating on a MYSQL Instance.

MYSQLInstance is the only class defined in this file.

MYSQLInstance: Derived class from Instance Base class, representing an
                MYSQL instance, and to perform operations on that instance

MYSQLInstance:
==============

    _get_instance_properties()      -- method to get the properties of the instance

    _restore_json()                 -- returns the apppropriate JSON request to pass for
    Restore In-Place

    restore_in_place()              -- Restores the mysql data/log files specified in the
    input paths list to the same location

    restore_out_of_place()          -- method to perform out of place restore of MySQL data/log/recurring files to the
    destination client.

    _restore_browse_option_json()   -- setter for  browse option  property in restore

    _restore_common_options_json()  -- setter for common options property in restore

    _restore_destination_json()     -- setter for destination options property in restore

    _restore_fileoption_json()      -- setter for file option property in restore

    _restore_admin_option_json()    -- setter for admin option property in restore

    _restore_mysql_option_json()    -- setter for MySQL restore option property in restore


MYSQLInstance instance Attributes:
==================================

    **port**                            -- Returns the MySQL Server Port number

    **mysql_username**                  -- Returns the MySQL Server username

    **nt_username**                     -- Returns the MySQL Server nt username

    **config_file**                     -- Returns the MySQL Server Config File location

    **binary_directory**                -- Returns the MySQL Server Binary File location

    **version**                         -- Returns the MySQL Server version number

    **log_data_directory**              -- Returns the MySQL Server log data directory

    **log_backup_sp_details**           -- Returns the MySQL Server Log backup SP details

    **command_line_sp_details**         -- Returns the MySQL Server commandline SP details

    **autodiscovery_enabled**           -- Returns the MySQL Server auto discovery enabled flag

    **xtrabackup_bin_path**             -- Returns the MySQL Server xtrabackup bin path

    **is_xtrabackup_enabled**           -- Returns the MySQL Server xtrabackup enabled flag

    **proxy_options**                   -- Returns the MySQL Server proxy options

    **mysql_enterprise_backup_binary_path** --  Returns the MySQL Enterprise backup binary path
    details

    **no_lock_status**                  --  Returns the No Lock check box status for MySQL Instance

    **ssl_enabled**                     --  Returns(boolean) True/False based on SSL status

"""

from __future__ import unicode_literals
from ..instance import Instance
from ..exception import SDKException
from ..credential_manager import Credential


class MYSQLInstance(Instance):
    """
    Class to represent a standalone MYSQL Instance
    """

    def __init__(self, agent_object, instance_name, instance_id=None):
        """Initialise the Subclient object.

            Args:
                agent_object    (object)  --  instance of the Agent class

                instance_name   (str)     --  name of the instance

                instance_id     (str)     --  id of the instance

                    default: None

            Returns:
                object - instance of the MYSQLInstance class

        """
        self._browse_restore_json = None
        self._commonoption_restore_json = None
        self._destination_restore_json = None
        self._fileoption_restore_json = None
        self._instance = None
        self.admin_option_json = None
        self.mysql_restore_json = None
        super(MYSQLInstance, self).__init__(agent_object, instance_name, instance_id)

    @property
    def port(self):
        """Returns the MySQL Server Port number.

        Returns:
            (str)   --  MySql server port number

        """
        return self._properties.get('mySqlInstance', {}).get('port', None)

    @property
    def mysql_username(self):
        """Returns the MySQL Server username.

        Returns:
            (str)   --  MySql server SA username

        """
        credential_name = self._properties.get('credentialEntity', {}).get('credentialName', None)
        return Credential(self._commcell_object, credential_name).credential_user_name

    @property
    def nt_username(self):
        """Returns the MySQL Server nt username.

        Returns:
            (str)   --  MySql server NT username

        """
        return self._properties.get('mySqlInstance', {}).get('NTUser', {}).get('userName', None)

    @property
    def config_file(self):
        """Returns the MySQL Server Config File location.

        Returns:
            (str)   --  MySql server config file location

        """
        return self._properties.get('mySqlInstance', {}).get('ConfigFile', None)

    @property
    def binary_directory(self):
        """Returns the MySQL Server Binary File location.

        Returns:
            (str)   --  MySql server binary directory

        """
        return self._properties.get('mySqlInstance', {}).get('BinaryDirectory', None)

    @property
    def version(self):
        """Returns the MySQL Server version number.

        Returns:
            (str)   --  MySql server version

        """
        return self._properties.get('mySqlInstance', {}).get('version', None)

    @property
    def log_data_directory(self):
        """Returns the MySQL Server log data directory.

        Returns:
            (str)   --  MySql server log directory path

        """
        return self._properties.get('mySqlInstance', {}).get('LogDataDirectory', None)

    @property
    def log_backup_sp_details(self):
        """Returns the MySQL Server Log backup SP details

        Returns:
            (dict)  --  MySql server log backup storage policy information

        """
        log_storage_policy_name = self._properties.get('mySqlInstance', {}).get(
            'logStoragePolicy', {}).get('storagePolicyName', None)
        log_storage_policy_id = self._properties.get('mySqlInstance', {}).get(
            'logStoragePolicy', {}).get('storagePolicyId', None)

        log_sp = {"storagePolicyName": log_storage_policy_name,
                  "storagePolicyId": log_storage_policy_id}
        return log_sp

    @property
    def command_line_sp_details(self):
        """Returns the MySQL Server commandline SP details.

        Returns:
            (dict)  --  MySql server commnadline storage policy information

        """
        cmd_storage_policy_name = self._properties.get('mySqlInstance', {}).get(
            'mysqlStorageDevice', {}).get('commandLineStoragePolicy', {}).get(
                'storagePolicyName', None)
        cmd_storage_policy_id = self._properties.get('mySqlInstance', {}).get(
            'mysqlStorageDevice', {}).get('commandLineStoragePolicy', {}).get(
                'storagePolicyId', None)

        command_sp = {"storagePolicyName": cmd_storage_policy_name,
                      "storagePolicyId": cmd_storage_policy_id}
        return command_sp

    @property
    def autodiscovery_enabled(self):
        """Returns the MySQL Server auto discovery enabled flag

        Returns:
            (bool)  --  True if auto discovery enabled
                        False if auto discovery not enabled

        """
        return self._properties.get('mySqlInstance', {}).get('EnableAutoDiscovery', False)

    @autodiscovery_enabled.setter
    def autodiscovery_enabled(self, value):
        """
        Sets the auto discovery attribute to True or False
        value (bool)   --  True or False

        """
        properties = self._properties
        update = {
            "EnableAutoDiscovery": value
        }
        properties['mySqlInstance'] = update
        self.update_properties(properties)

    @property
    def xtrabackup_bin_path(self):
        """Returns the MySQL Server xtrabackup bin path

        Returns:
            (str)   --  MySql server xtrabackup binary path

        """
        return self._properties.get(
            'mySqlInstance', {}).get(
                'xtraBackupSettings', {}).get('xtraBackupBinPath', "")

    @property
    def is_xtrabackup_enabled(self):
        """Returns the MySQL Server xtrabackup enabled flag

        Returns:
            (bool)  --  True if xtrabackup is enabled
                        False if xtrabackup is not enabled

        """
        return self._properties.get(
            'mySqlInstance', {}).get(
                'xtraBackupSettings', {}).get('enableXtraBackup', False)

    @property
    def proxy_options(self):
        """Returns the MySQL Server proxy options

        Returns:
            (dict)  --  MySql server proxy information

        """
        proxy_settings = self._properties.get('mySqlInstance', {}).get('proxySettings', {})
        proxy_opt = {
            "isUseSSL": proxy_settings.get('isUseSSL', False),
            "isProxyEnabled": proxy_settings.get('isProxyEnabled', False),
            "runBackupOnProxy": proxy_settings.get('runBackupOnProxy', False),
            "instanceId": proxy_settings.get('proxyInstance', {}).get('instanceId', None),
            "instanceName": proxy_settings.get('proxyInstance', {}).get('instanceName', None),
            "clientId": proxy_settings.get('proxyInstance', {}).get('clientId', None),
            "clientName": proxy_settings.get('proxyInstance', {}).get('clientName', None)}
        return proxy_opt

    @property
    def mysql_enterprise_backup_binary_path(self):
        """ Returns the MySQL Enterprise backup binary path detail

            Return Type: dict

        """
        meb_settings = self._properties.get('mySqlInstance', {}).get('mebSettings', {})
        return meb_settings

    @mysql_enterprise_backup_binary_path.setter
    def mysql_enterprise_backup_binary_path(self, value):
        """ Setter for MySQL Enterprise backup binary path

            Args:

                value (str)  -- Contains the MySQL Enterprise backup binary path to be updated
                in MySQL Instance property

        """
        if not isinstance(value, str):
            raise SDKException('Instance', '101')
        properties = self._properties
        meb_bin_path_update = {
            "enableMEB": False if value == '' else True,
            "mebBinPath": value
        }
        properties['mySqlInstance']['mebSettings'] = meb_bin_path_update
        self.update_properties(properties)

    @property
    def no_lock_status(self):
        """ Returns the status of No Lock Checkbox in MySQL Instance

            Returns:
            (bool)  --  True if No Lock checkbox is enabled
                        False if No Lock checkbox is disabled

        """
        return self._properties.get('mySqlInstance', {}).get('EnableNoLocking', False)

    @no_lock_status.setter
    def no_lock_status(self, value):
        """ Setter for No Lock property in MySQL Instance

            Args:

                value (bool)  -- True or False to enable or disable the No Lock
                property in MySQL Instance

        """
        if not isinstance(value, bool):
            raise SDKException('Instance', '101')
        properties = self._properties
        properties['mySqlInstance']['EnableNoLocking'] = value
        self.update_properties(properties)

    @property
    def ssl_enabled(self):
        """ Returns(boolean) True/False based on SSL status """
        return self._properties.get('mySqlInstance', {}).get('sslEnabled', False)

    def _get_instance_properties(self):
        """Gets the properties of this instance.

            Raises:
                SDKException:
                    if response is empty

                    if response is not success

        """
        super(MYSQLInstance, self)._get_instance_properties()
        self._instance = {
            "type": 0,
            "clientName": self._agent_object._client_object.client_name,
            "clientSidePackage": True,
            "subclientName": "",
            "backupsetName": "defaultDummyBackupSet",
            "instanceName": self.instance_name,
            "appName": self._agent_object.agent_name,
            "consumeLicense": True
        }

    def _restore_json(self, **kwargs):
        """Returns the JSON request to pass to the API as per the options selected by the user.

            Args:
                kwargs   (list)  --  list of options need to be set for restore

            Returns:
                dict - JSON request to pass to the API

        """
        rest_json = super(MYSQLInstance, self)._restore_json(**kwargs)
        restore_option = {}
        if kwargs.get("restore_option"):
            restore_option = kwargs["restore_option"]
            for key in kwargs:
                if not key == "restore_option":
                    restore_option[key] = kwargs[key]
        else:
            restore_option.update(kwargs)

        if restore_option["from_time"] is None:
            restore_option["from_time"] = {}

        if restore_option["to_time"] is None:
            restore_option["to_time"] = {}

        self._restore_admin_option_json(restore_option)
        self._restore_mysql_option_json(restore_option)
        rest_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][
            "mySqlRstOption"] = self.mysql_restore_json
        rest_json["taskInfo"]["subTasks"][0]["options"]["adminOpts"] = self.admin_option_json
        return rest_json

    def restore_in_place(
            self,
            path=None,
            staging=None,
            dest_client_name=None,
            dest_instance_name=None,
            data_restore=True,
            log_restore=False,
            overwrite=True,
            copy_precedence=None,
            from_time=None,
            to_time=None,
            media_agent=None,
            table_level_restore=False,
            clone_env=False,
            clone_options=None,
            redirect_enabled=False,
            redirect_path=None,
            browse_jobid=None):
        """Restores the mysql data/log files specified in the input paths list to the same location.

            Args:
                path                    (list)  --  list of database/databases to be restored

                    default: None

                staging                 (str)   --  staging location for mysql logs during restores

                    default: None

                dest_client_name        (str)   --  destination client name where files are to be
                restored

                    default: None

                dest_instance_name      (str)   --  destination mysql instance name of destination
                client

                    default: None

                data_restore            (bool)  --  for data only/data+log restore

                    default: True

                log_restore             (bool)  --  for log only/data+log restore

                    default: False

                overwrite               (bool)  --  unconditional overwrite files during restore

                    default: True

                copy_precedence         (int)   --  copy precedence value of storage policy copy

                    default: None

                from_time               (str)   --  time to retore the contents after
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                to_time                 (str)   --  time to retore the contents before
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                media_agent             (str)   --  media agent associated

                    default: None

                table_level_restore     (bool)  --  Table level restore flag

                    default: False

                clone_env               (bool)  --  boolean to specify whether the database
                should be cloned or not

                    default: False

                clone_options           (dict)  --  clone restore options passed in a dict

                    default: None

                    Accepted format: {
                                        "stagingLocaion": "/gk_snap",
                                        "forceCleanup": True,
                                        "port": "5595",
                                        "libDirectory": "",
                                        "isInstanceSelected": True,
                                        "reservationPeriodS": 3600,
                                        "user": "",
                                        "binaryDirectory": "/usr/bin"

                                     }

                redirect_enabled         (bool)  --  boolean to specify if redirect restore is
                enabled

                    default: False

                redirect_path           (str)   --  Path specified in advanced restore options
                in order to perform redirect restore

                    default: None

                browse_jobid           (int)   --  Browse jobid to browse and restore from

                    default: None

            Returns:
                object - instance of the Job class for this restore job

            Raises:
                SDKException:
                    if paths is not a list

                    if failed to initialize job

                    if response is empty

                    if response is not success

        """
        if not (isinstance(path, list) and
                isinstance(overwrite, bool)):
            raise SDKException('Instance', '101')

        if path == []:
            raise SDKException('Instance', '104')

        if dest_client_name is None:
            dest_client_name = self._agent_object._client_object.client_name

        if dest_instance_name is None:
            dest_instance_name = self.instance_name

        request_json = self._restore_json(
            paths=path,
            staging=staging,
            dest_client_name=dest_client_name,
            dest_instance_name=dest_instance_name,
            data_restore=data_restore,
            log_restore=log_restore,
            overwrite=overwrite,
            copy_precedence=copy_precedence,
            from_time=from_time,
            to_time=to_time,
            media_agent=media_agent,
            table_level_restore=table_level_restore,
            clone_env=clone_env,
            clone_options=clone_options,
            redirect_enabled=redirect_enabled,
            redirect_path=redirect_path,
            browse_jobid=browse_jobid)

        return self._process_restore_response(request_json)

    def restore_out_of_place(
            self,
            path=None,
            staging=None,
            dest_client_name=None,
            dest_instance_name=None,
            data_restore=True,
            log_restore=False,
            overwrite=True,
            copy_precedence=None,
            from_time=None,
            to_time=None,
            media_agent=None,
            table_level_restore=False,
            clone_env=False,
            clone_options=None,
            redirect_enabled=False,
            redirect_path=None,
            browse_jobid=None,
            recurringRestore=False):
        """
        Method to perform out of place restore of MySQL data/log/recurring files to the destination client location.

            Args:
                path                    (list)  --  list of database/databases to be restored
                staging                 (str)   --  staging location for mysql logs during restores
                dest_client_name        (str)   --  destination client name where files are to be restored
                dest_instance_name      (str)   --  destination mysql instance name of destination client
                data_restore            (bool)  --  for data only/data+log restore
                log_restore             (bool)  --  for log only/data+log restore
                overwrite               (bool)  --  unconditional overwrite files during restore
                copy_precedence         (int)   --  copy precedence value of storage policy copy
                from_time               (str)   --  time to restore the contents after
                        format: YYYY-MM-DD HH:MM:SS
                to_time                 (str)   --  time to restore the contents before
                        format: YYYY-MM-DD HH:MM:SS
                media_agent             (str)   --  media agent associated
                table_level_restore     (bool)  --  Table level restore flag
                clone_env               (bool)  --  boolean to specify whether the database should be cloned or not
                clone_options           (dict)  --  clone restore options passed in a dict
                redirect_enabled         (bool)  --  boolean to specify if redirect restore is enabled
                redirect_path           (str)   --  Path specified in advanced restore options
                in order to perform redirect restore
                browse_jobid           (int)   --  Browse jobid to browse and restore from
                recurringRestore       (bool)  --  for Recurring restore

            Returns:
                object - instance of the Job class for this restore job

            Raises:
                SDKException:
                    if paths is not a list

                    if failed to initialize job

                    if destination client name is empty

                    if destination Instance name empty

                    if response is empty

                    if response is not success
        """
        if not (isinstance(path, list) and
                isinstance(overwrite, bool)):
            raise SDKException('Instance', '101')

        if path == []:
            raise SDKException('Instance', '104')

        if dest_client_name is None:
            raise SDKException('Client', '102',
                               "The destination client name is missing. "
                               "Please provide a valid destination client name to proceed")

        if dest_instance_name is None:
            raise SDKException('Instance', '102',
                               "The destination Instance name is missing. "
                               "Please provide a valid destination Instance name to proceed")

        request_json = self._restore_json(
            paths=path,
            staging=staging,
            dest_client_name=dest_client_name,
            dest_instance_name=dest_instance_name,
            data_restore=data_restore,
            log_restore=log_restore,
            overwrite=overwrite,
            copy_precedence=copy_precedence,
            from_time=from_time,
            to_time=to_time,
            media_agent=media_agent,
            table_level_restore=table_level_restore,
            clone_env=clone_env,
            clone_options=clone_options,
            redirect_enabled=redirect_enabled,
            redirect_path=redirect_path,
            browse_jobid=browse_jobid,
            recurringRestore=recurringRestore)

        return self._process_restore_response(request_json)

    def _restore_browse_option_json(self, value):
        """setter for the Browse options for restore in Json"""

        if not isinstance(value, dict):
            raise SDKException('Instance', '101')
        super(MYSQLInstance, self)._restore_browse_option_json(value)
        self._browse_restore_json['backupset'] = {
            "clientName": self._agent_object._client_object.client_name,
            "backupsetName": "defaultDummyBackupSet"
        }

        if value.get("browse_jobid"):
            self._browse_restore_json['browseJobId'] = value.get("browse_jobid")

        if value.get("from_time") and value.get("to_time"):
            self._browse_restore_json["timeRange"] = {"fromTime" : value.get("from_time"),
                                                      "toTime" : value.get("to_time")}

    def _restore_common_options_json(self, value):
        """setter for the Common options in restore JSON"""

        if not isinstance(value, dict):
            raise SDKException('Instance', '101')

        self._commonoption_restore_json = {
            "restoreToDisk": False,
            "onePassRestore": False,
            "revert": False,
            "syncRestore": False
        }

    def _restore_destination_json(self, value):
        """setter for the MySQL Destination options in restore JSON"""

        if not isinstance(value, dict):
            raise SDKException('Instance', '101')

        self._destination_restore_json = {
            "destinationInstance": {
                "clientName": value.get("dest_client_name", ""),
                "instanceName": value.get("dest_instance_name", ""),
                "appName": "MySQL"
            },
            "destClient": {
                "clientName": value.get("dest_client_name", "")
            }
        }

    def _restore_fileoption_json(self, value):
        """setter for the fileoption restore option in restore JSON"""

        if not isinstance(value, dict):
            raise SDKException('Instance', '101')

        self._fileoption_restore_json = {
            "sourceItem": value.get("paths", [])
        }

    def _restore_admin_option_json(self, value):
        """setter for the admin restore option in restore JSON"""

        if not isinstance(value, dict):
            raise SDKException('Instance', '101')

        self.admin_option_json = {
            "contentIndexingOption": {
                "subClientBasedAnalytics": False
            }
        }

    def _restore_mysql_option_json(self, value):
        """setter for the mysql restore option in restore JSON"""

        if not isinstance(value, dict):
            raise SDKException('Instance', '101')

        self.mysql_restore_json = {
            "destinationFolder": "",
            "data": value.get("data_restore", True),
            "log": value.get("log_restore", True),
            "recurringRestore": value.get("recurringRestore", False),
            "temporaryStagingLocation": value.get("staging", ""),
            "dataStagingLocation": "",
            "logRestoreType": 0,
            "tableLevelRestore": value.get("table_level_restore", False),
            "pointofTime": True if value.get("to_time") else False,
            "instanceRestore": True,
            "isCloneRestore": value.get("clone_env", False),
            "fromTime": value.get("from_time", {}),
            "refTime": value.get("to_time", {}),
            "destinationServer": {
                "name": ""
            }
        }
        if value.get("table_level_restore"):
            self.mysql_restore_json['dropTable'] = True
            self.mysql_restore_json['instanceRestore'] = False

        if value.get("clone_env", False):
            self.mysql_restore_json["cloneOptions"] = value.get("clone_options", "")

        if value.get("redirect_path"):
            self.mysql_restore_json["redirectEnabled"] = True
            self.mysql_restore_json["redirectItems"] = [value.get("redirect_path")]

        if value.get("from_time"):
            self.mysql_restore_json["fromTime"] = {"time" : value.get("to_time")}

        if value.get("to_time"):
            self.mysql_restore_json["refTime"] = {"time" : value.get("to_time")}

        if value.get("to_time"):
            self.mysql_restore_json["pointInTime"] = {"time" : value.get("to_time")}

        if value.get("dest_instance_name"):
            self.mysql_restore_json["destinationServer"] = {"name": value.get(
                "dest_instance_name")}
