#
# Copyright 2018-2021 VMware, Inc.  All rights reserved. -- VMware Confidential
#
"""PatchRunner integration hook for Perfchart Service

This module integrates Perfchart Service (Perfcharts) patching scripts with the
PatchRunner Framework.

"""
__author__ = 'VMware, Inc.'
__copyright__ = 'Copyright 2017-2021 VMware, Inc. All rights reserved.'

import logging
import os
import sys

from extensions import extend, Hook
from l10n import msgMetadata as _T, localizedString as _
from reporting import getProgressReporter
from patch_errors import UserError
import vcsa_utils
sys.path.append(os.path.join(os.path.dirname(__file__), "patches"))
import utils
from cis.defaults import get_component_home_dir
patches = [
    # In order to update from version 0 to version 1, execute patch_01.py
    ("1", "patch_01"),
    ("2", "patch_02"),
    ("3", "patch_03"),
    ("4", "patch_04"),
]

COMPONENT_NAME = "perfcharts"

logger = logging.getLogger(__name__)

backup_file = 'perfcharts.properties'
perfcharts_props_file = '%s/perfcharts.properties' % utils.get_config_dir()

@extend(Hook.Discovery)
def discover(ctx):
    logger.info("Executing Discovery Hook for Perfchart Service")

    utils.preserveVersionFile(ctx.stageDirectory)

    if is_infra_node():
        logger.info("component is not part of this deployment type, hence its "
                    "patch logic will not be applied")
        return None
    if not is_patch_needed(perfcharts_props_file):
        logger.info("perfchart.patch: Patch version up-to-date.")
        return None
    return vcsa_utils.getComponentDiscoveryResult(COMPONENT_NAME,
                                                  displayName=_(
                                                   _T("perfchart.displayName",
                                                   "Perfchart Service")))

@extend(Hook.Expand)
def expand(ctx):
    '''void expand(PatchContext ctx) throw UserError'''
    if not vcsa_utils.isDisruptiveUpgrade(ctx):
        current_version = utils.getSourceVersion(ctx.stageDirectory)
        progressReporter = getProgressReporter()
        progressReporter.updateProgress(0, _(_T("perfcharts.expand.begin", 'Start perfcharts expand')))
        logger.info("Prepare for the incremental patching of perfcharts service")
        _doIncrementalExpand(ctx)
        progressReporter.updateProgress(100, _(_T("perfcharts.expand.complete", 'expand perfcharts completed')))

@extend(Hook.Contract)
def contract(ctx):
    '''void patch(PatchContext ctx) throw UserError'''
    if not vcsa_utils.isDisruptiveUpgrade(ctx):
        progressReporter = getProgressReporter()
        progressReporter.updateProgress(0, _(_T("perfcharts.contract.begin", 'Start perfcharts contract')))
        _doIncrementalContract(ctx)
        progressReporter.updateProgress(100, _(_T("perfcharts.contract.complete", 'contract perfcharts completed')))

@extend(Hook.Patch)
def execute_patch(ctx):
    #This hook is invoked after the rpms are installed.
    if vcsa_utils.isDisruptiveUpgrade(ctx):
        logger.info("Executing Patch Hook for Perfchart Service")
        progressReporter = getProgressReporter()
        progressReporter.updateProgress(0, _(_T("perfchart.patch.begin",
                                            "Start Perfchart patching")))
        _do_incremental_patching(ctx)
        progressReporter.updateProgress(100, _(_T("perfchart.patch.complete",
                                              "patching Perfchart completed")))

def _do_incremental_patching(ctx):
    for ver, modulePath in patches:
        logger.info("Checking if need patch %s on version %s"
                     % (ver, modulePath))
        mod = __import__(modulePath)

        backupfilePath = os.path.join(ctx.stageDirectory, backup_file)
        if mod.is_patch_needed(backupfilePath):
            logger.info("Patch %s needed" % (modulePath))
            mod.doPatching(ctx)
            logger.info('Patch %s applied' % (modulePath))
            utils.update_patch_version(ver, backupfilePath)
    # Delete rpmnew files if still existing.
    propFileNew = os.path.join(utils.get_config_dir(),
                               'perfcharts.properties.rpmnew')
    if os.path.isfile(propFileNew):
        os.remove(propFileNew)
    catalinaPropNew = os.path.join(utils.get_tc_instance_dir(), 'conf',
                                   'catalina.properties.rpmnew')
    if os.path.isfile(catalinaPropNew):
        os.remove(catalinaPropNew)
    wrapperConfNew = os.path.join(get_component_home_dir(COMPONENT_NAME),
                                  'wrapper', 'conf', 'wrapper.conf.rpmnew')
    if os.path.isfile(wrapperConfNew):
        os.remove(wrapperConfNew)


def is_patch_needed(prop_file):
    for ver, modulePath in patches:
        logger.info("Checking if need patch %s on version %s"
                     % (ver, modulePath))
        mod = __import__(modulePath)
        if hasattr(mod, 'is_patch_needed'):
            if mod.is_patch_needed(prop_file):
                return True
    return False

def is_infra_node():
    node_type = vcsa_utils.getDeploymentType()
    logger.info('Node type is %s' % node_type)
    return node_type == vcsa_utils.INFRASTRUCTURE_DEPLOYMENT_TYPE

def _doIncrementalExpand(ctx):
    currentVersion = int(utils.getSourceVersion(ctx.stageDirectory))
    user_error = None
    for ver, modulePath in getApplicablePatches(currentVersion):
        logger.info('Applying expand for perfcharts %s on version %s' % (ver, modulePath))
        try:
            mod = __import__(modulePath)
        except Exception as e:
            err_msg = "Failed to import perfcharts patch module %s! Error: %s."
            logger.error(err_msg % (modulePath, str(e)))
            cause = 'perfcharts.patch.import.fail: {} for {}'.format(err_msg, modulePath)
            user_error = UserError(cause)
            break
        mod.doExpand(ctx)
        logger.info('Expand %s applied' % (modulePath))
    if not user_error:
        logger.info("All Expand patches applied successfully")
    else:
        logger.error("Failed to expand perfcharts service")
        raise user_error

def _doIncrementalContract(ctx):
    init_version = int(utils.getSourceVersion(ctx.stageDirectory))

    # Nothing to contract if it is a new service
    if init_version is None:
        return
    user_error = None
    for ver, modulePath in reversed(getApplicablePatches(init_version)):
        logger.info('Applying contract for perfcharts %s on version %s' % (ver, modulePath))
        try:
            mod = __import__(modulePath)
        except Exception as e:
            err_msg = "Failed to import perfcharts patch module %s! Error: %s."
            logger.error(err_msg % (modulePath, str(e)))
            cause = 'perfcharts.patch.import.fail: {} for {}'.format(err_msg, modulePath)
            user_error = UserError(cause)
            break
        mod.doContract(ctx)
        logger.info('Contract %s applied' % (modulePath))
    if not user_error:
        logger.info("All Contract patches applied successfully")
    else:
        logger.error("Failed to contract perfcharts service")
        raise user_error

def getApplicablePatches(sourceVersion):
    if sourceVersion is None:
        return patches
    return [(ver, modulePath) for (ver, modulePath) in patches if int(ver) > int(sourceVersion)]

