# Copyright (C) 2017-2021 VMware, Inc.  All rights reserved.
# -- VMware Confidential

import logging
import vcsa_utils
from extensions import extend, Hook
from fss_utils import getTargetFSS
from l10n import msgMetadata as _T, localizedString as _
from reporting import getProgressReporter

import sys
from os.path import dirname, realpath, join, isfile

payloadDir = dirname(realpath(__file__))
patchesDir = join(payloadDir, "patches")

sys.path.append(payloadDir)
sys.path.append(patchesDir)


logger = logging.getLogger(__name__)

import utils


@extend(Hook.Discovery)
def discover(ctx):
    "DiscoveryResult discover(PatchContext ctx) throw UserError"

    logger.info("Envoy ADS Extension Discovery Hook begins")
    # in case we are not under FSS
    replicationConfig = {}
    if getTargetFSS("NDU_Limited_Downtime"):
       utils.preserveVersionFile(ctx.stageDirectory)
       replicationConfig = {
          # exclude specific configs
          "/etc/vmware-envoy/config.yaml": None,
          "/etc/vmware-envoy/config.cfg": None,
       }

    return vcsa_utils.getComponentDiscoveryResult(
        "rhttpproxy", displayName=_(_T("rhttpproxy.b2b.patch.name",
                                       "VMware vCenter Envoy ADS")),
        replicationConfig=replicationConfig)


@extend(Hook.Prepatch)
def prePatch(ctx):
    "void prePatch(PatchContext sharedCtx) throw UserError"

    if not getTargetFSS("NDU_Limited_Downtime"):
       logger.info("Envoy ADS Extension PrePatch Hook begins")
       utils.preserveVersionFile(ctx.stageDirectory)


def _applyPatch(ctx, modulePath, ver):
    logger.info("Applying patch {0} for version {1}".format(modulePath, ver))
    mod = __import__(modulePath)
    mod.doPatching(ctx)
    logger.info("Patch {0} applied".format(modulePath))


def _doIncrementalPatching(ctx):
    currVersion = utils.getSourceVersion(ctx.stageDirectory)
    logger.info("Current version is {}".format(currVersion))

    for ver, modulePath in patches:
        if currVersion is None or currVersion < ver:
            _applyPatch(ctx, modulePath, ver)

    utils.setSourceVersion(getLatestVersion())


# If a new version is added here, this version has to be updated in:
#    bora/vim/apps/rhttpproxy/version.txt
patches = [
    ("7.0.1.00000", "patch02"),
    ("7.0.2.00000", "patch03"),
]


def getLatestVersion():
    return patches[-1][0]


@extend(Hook.Patch)
def patch(ctx):
    "void patch(PatchContext ctx) throw UserError"

    logger.info("Envoy ADS Extension Patch Hook begins")

    # Always revert TLS settings to default:
    if not getTargetFSS("NDU_Limited_Downtime"):
       progressReporter = getProgressReporter()

       progressReporter.updateProgress(0)
       _applyPatch(ctx, "patch01", "all")
       progressReporter.updateProgress(50)
       _doIncrementalPatching(ctx)
       progressReporter.updateProgress(100)


@extend(Hook.Requirements)
def collectRequirements(ctx):
    '''RequirementsResult collectRequirements(PatchContext ctx)'''
    logger.info("Envoy ADS Requirements hook is no-op")
    pass

@extend(Hook.Validation)
def validate(ctx):
    '''ValidationResult validate(PatchContext ctx)'''
    logger.info("Envoy ADS Validation hook is no-op")
    pass

def _expand(ctx, modulePath, ver):
    logger.info("Applying patch {0} for version {1}".format(modulePath, ver))
    mod = __import__(modulePath)
    mod.doExpand(ctx)
    logger.info("Patch {0} applied".format(modulePath))


def _doIncrementalExpand(ctx):
    currVersion = utils.getSourceVersion(ctx.stageDirectory)
    logger.info("Current version is {}".format(currVersion))

    for ver, modulePath in patches:
        if currVersion is None or currVersion < ver:
            _expand(ctx, modulePath, ver)

    utils.setSourceVersion(getLatestVersion())

@extend(Hook.Expand)
def expand(ctx):
    '''void prepare(PatchContext ctx) throw UserError'''
    # Always revert TLS settings to default:
    if getTargetFSS("NDU_Limited_Downtime"):
       progressReporter = getProgressReporter()

       progressReporter.updateProgress(0)
       _expand(ctx, "patch01", "all")
       progressReporter.updateProgress(50)
       _doIncrementalExpand(ctx)
       progressReporter.updateProgress(100)


@extend(Hook.Contract)
def contract(ctx):
    '''void patch(PatchContext ctx) throw UserError'''
    logger.info("Envoy ADS Contract hook is no-op")
    # We likely won't need this any time soon as all of our changes
    # happen in config files, before start-up.
    # This phase is strictly after start up.
    # Also the files can only be changed by admin, not by a service.
    pass

# If a new patch is added, update the tmp here.
# This method will allow for having more than one tmp file
tmpFiles = [
    ("/etc/vmware-rhttpproxy/config.tmp3", "patch03"),
    ("/etc/vmware-rhttpproxy/config.tmp2", "patch02"),
    ("/etc/vmware-rhttpproxy/config.tmp1", "patch01"),
]

def _revert(ctx, modulePath):
    logger.info("Reverting patch {0}".format(modulePath))
    mod = __import__(modulePath)
    mod.doRevert(ctx)
    logger.info("Revert for Patch {0} applied".format(modulePath))


def _doIncrementalRevert(ctx):
    progressReporter = getProgressReporter()
    progress = 0
    for file, modulePath in tmpFiles:
       if isfile(file):
          _revert(ctx, modulePath)
          # Arbitrary, change according to total patch number
          progress += 30
          progressReporter.updateProgress(progress)
    progressReporter.updateProgress(100)

@extend(Hook.Revert)
def revert(ctx):
    '''void onSuccess(PatchContext ctx) throw UserError'''
    # No need to check for FSS here, only called in NDU
    _doIncrementalRevert(ctx)
