"""
Copyright 2016 VMware, Inc.  All rights reserved. -- VMware Confidential

This file provides the hooks which will be invoked during patching process.
"""
import logging
import os
import platform
import sys
import shutil
import subprocess

from extensions import extend, Hook
from l10n import msgMetadata as _T, localizedString as _
from reporting import getProgressReporter

from . import utils
import vcsa_utils

# Note: The relative tag paths starting with "." are not supported
#       and the paths should not include root's tag.

# The elements to be updated in extension.xml
EXTENSION_VERSION_TAG_PATH = "extension/version"
CLIENT_VERSION_TAG_PATH = "extension/clients/client/version"

# The elements to be updated/set in vci-integrity.xml
MIN_HOST_VERSION_TAG_PATH = "HostUpgradeConfig/MinHostVersion"
PACKAGE_VERSIONS_TAG_PATH = "HostUpgradeConfig/PackageVersions"
VIM_NAME_SPACE_TAG_PATH = "plugins/vci_vcIntegrity/vimNameSpace"
HOST_INCLUDE_TAG_PATH = "contentSettings/hostInclude"

logger = logging.getLogger(__name__)
MY_PAYLOAD_DIR = os.path.dirname(__file__)
VCINTEGRITY_LOGGING_FOLDER = os.path.join(os.sep, "var", "log", "vmware", "vmware-updatemgr")

PATCHES = [("1.0", "patch1_0"),
           ("2.0", "patch2_0"),
           ("3.0", "patch3_0")
          ]
VERSION_TXT = "version.txt"

@extend(Hook.Discovery)
def discover(patch_context):
    """
    Determine whether the component should be patched.

    :param patch_context: Context given by the patch framework

    :return: 'None' if no patching should be done, otherwise component discovery
             result for VUM
    """
    if platform.system().lower() == "windows":
        return None

    # need to save current version.txt file because it is already replaced at Patch stage
    curVersion = utils.GetConfigVersion(utils.CONFIG_VERSION_FILE)
    filePath = os.path.join(patch_context.stageDirectory, VERSION_TXT)
    utils.SetConfigVersion(filePath, curVersion)

    return vcsa_utils.getComponentDiscoveryResult("updatemgr")


@extend(Hook.Patch)
def patch(ctx):
    progressReporter = getProgressReporter()
    progressReporter.updateProgress(0, _(_T("updatemgr.patch.begin",
                                            "Start VMware Update Manager"
                                            " patching")))
    _doIncrementalPatching(ctx, progressReporter)
    _updateExtensionConfig()
    _updateIntegrityConfig()
    _upgradeDatabaseSchema()
    _upgradeSecurityConfig()
    progressReporter.updateProgress(100, _(_T("updatemgr.patch.complete",
                                              "VMware Update Manager patching"
                                              " completed")))
    logger.info("VMware Update Manager is successfully patched")


def _doIncrementalPatching(ctx, progressReporter):
    if not PATCHES:
        return

    logger.info("Running Incremental Patching ...")
    applicablePatches = list()
    # Import patches python directory where all individual python module reside
    sys.path.append(os.path.join(MY_PAYLOAD_DIR, "patches"))

    # Read current version from saved version.txt
    filePath = os.path.join(ctx.stageDirectory, VERSION_TXT)
    curVersion = utils.GetConfigVersion(filePath)
    logger.info("Current version = %s", str(curVersion))
    for ver, modulePath in PATCHES:
        version = utils.Version(ver)
        logger.info("Patch version = %s", str(version))
        if curVersion < version:
            logger.info("Add patch %s", modulePath)
            applicablePatches.append((ver, modulePath))

    if len(applicablePatches) == 0:
        return

    logger.info("Applying Patching ...")
    appliedPatchCount = 0
    totalPatch = len(applicablePatches)
    updatedVer = None
    for ver, modulePath in sorted(applicablePatches,
                                  key=lambda x: utils.Version(x[0])):
        logger.info("Applying patch %s for version %s" % (modulePath, ver))
        mod = __import__(modulePath)
        mod.doPatching(ctx)
        logger.info("Patch %s applied" % (modulePath))
        appliedPatchCount += 1
        updatedVer = ver
        progressReporter.updateProgress((appliedPatchCount*90)/totalPatch)


def _updateExtensionConfig():
    # If VUM RPM in source and target build are from same build, then RPM
    # upgrade become a no-op and no new config files copies are generated. If no
    # new config files are present (implying VUM RPM is not updated), then will
    # not execute logic to update the product version.
    if not os.path.exists(utils.UPDATED_EXTENTION_XML):
        logger.info('%s does not exist. Skip updating extension config'
                    % utils.UPDATED_EXTENTION_XML)
        return

    # Update extension.xml from extension.xml.rpmnew
    xmlMerger = utils.XmlMerger(utils.EXTENTION_XML, utils.UPDATED_EXTENTION_XML)

    # Update extension/version
    xmlMerger.incrementVersion(EXTENSION_VERSION_TAG_PATH)

    # Update extension/clients/client/version
    # Note: Re-implement this method if we have more than one client.
    xmlMerger.incrementVersion(CLIENT_VERSION_TAG_PATH)

    # Save changes into extension.xml
    xmlMerger.saveToFile()

def _updateIntegrityConfig():
    # If VUM RPM in source and target build are from same build, then RPM
    # upgrade become a no-op and no new config files copies are generated. If no
    # new config files are present (implying VUM RPM is not updated), then will
    # not execute logic to update the package version.
    if not os.path.exists(utils.UPDATED_INTEGRITY_XML):
        logger.info('%s does not exist. Skip updating integrity Config'
                    % utils.UPDATED_INTEGRITY_XML)
        return

    # Update vci-integrity.xml from vci-integrity.xml.rpmnew
    xmlMerger = utils.XmlMerger(utils.INTEGRITY_XML, utils.UPDATED_INTEGRITY_XML)

    # Update PackageVersions and MinHostVersion
    xmlMerger.incrementVersion(PACKAGE_VERSIONS_TAG_PATH)
    xmlMerger.incrementVersion(MIN_HOST_VERSION_TAG_PATH)

    # Update VIM API version
    xmlMerger.update(VIM_NAME_SPACE_TAG_PATH)

    # Replace all hostInclude instances with those from vci-integrity.xml.rpmnew
    xmlMerger.replace(HOST_INCLUDE_TAG_PATH)

    # Save changes into vci-integrity.xml
    xmlMerger.saveToFile()

def _upgradeDatabaseSchema():
    # upgrade database schema to the latest version

    # Check if file vciInstallUtils_config.xml exits
    if not os.path.exists(utils.INSTALLUTILS_CONFIG_XML):
        logger.info("InstallUtils config file doesn't exist. Skipping database"
                     " schema upgrade.")
        return

    # Run Install Util with dbupgrade
    command = utils.CreateUpgradeDataBaseSchemaCmd(VCINTEGRITY_LOGGING_FOLDER)

    logger.info("Run Database schema upgrade ....")
    rc = subprocess.call(command)
    if rc:
        logger.error("Failed to upgrade database schema. return code:"
                      " %s" % (rc))
    else:
        logger.info("Database schema upgraded successfully.")

def _upgradeSecurityConfig():
    # upgrade security config
    # 1) TLS protocols in configuration files (vci-integrity, jetty-vum-ssl)

   utils.upgradeVUMTLSConfig()
   logger.info("Security configuration upgraded successfully")
