# Copyright 2020 VMware, Inc.
# All rights reserved. -- VMware Confidential
"""
The module contains logic to upgrade vmc-gateway to cloud-services-gateway.
"""
import sys
import os
import json
import logging
import subprocess
import vcsa_utils
import fss_utils
from patch_specs import DiscoveryResult, ValidationResult, Mismatch, Requirements, PatchInfo, \
    RequirementsResult
from extensions import extend, Hook
from l10n import msgMetadata as _T, localizedString as _
import uuid

logger = logging.getLogger(__name__)

BUILD_INFO_FILE = '/etc/vmware/.buildInfo'

PRODUCTS_FILE = '/etc/vmware/products.conf'

FB_STATUS_FILE = '/var/log/firstboot/fbInstall.json'

UPDATE_CONF_FILE = '/etc/applmgmt/appliance/update.conf'

HLM_UPDATE_CONF_DIR = '/etc/vmware/products/hlm'

GW_PLATFORM_UPDATE_FSS = 'GW_PLATFORM_UPDATE'

GATEWAY_UUID_FILE = '/etc/vmware/appliance/appliance.cfg'

MACHINE_ID_CMD = '/usr/lib/vmware-vmafd/bin/vmafd-cli \
get-machine-id --server-name localhost'

INTERNAL_ERROR_TEXT = _(_T('ur.internal.text',
                           'Internal error occurs during execution'
                            'of update process.'))
INTERNAL_ERROR_RESOLUTION = _(_T('ur.internal.resolution',
                                 'Send upgrade log files to VMware technical support '
                                 'team for further assistance.'))

def isGateway():
    ''' Indicate if the source is gateway or not
    '''
    if os.path.isfile(BUILD_INFO_FILE):
        with open(BUILD_INFO_FILE, 'r') as fp:
            if 'CLOUDVM_NAME:VMware-vCenter-Cloud-Gateway' in fp.read():
                logger.info('Running on a VMC Gateway appliance.')
                return True
            logger.info('Not running on a VMC Gateway appliance.')
            return False
    else:
        logger.warning('File %s does not exist', BUILD_INFO_FILE)
        return False

def isHlmInstalled():
    '''
    Indicate if HLM is installed on this gateway
    :return: return True is HLM is installed on this gateway else False
    '''
    logger.info('Checking if HLM is installed')
    productEnabled = False
    if os.path.exists(FB_STATUS_FILE):
        with open(FB_STATUS_FILE) as fp:
            content = json.load(fp)
        if 'success' in content.get('status', None):
            productEnabled = True
            logger.info('HLM is installed')
    return productEnabled

@extend(Hook.Discovery)
def discover(ctx):  # pylint: disable=W0613
    '''DiscoveryResult discover(PatchContext sharedCtx) throw UserUpgradeError'''

    # Script is only applicable for Gateway
    if not isGateway():
        return None

    return vcsa_utils.getComponentDiscoveryResult("vmware-cis-config",
                                                  componentId="vmc_gateway",
                                                  displayName=_(_T("gateway.comp.displayName",
                                                                   "VMC Gateway transition")))

@extend(Hook.Requirements)
def collectRequirements(ctx):  # pylint: disable=W0613
    '''RequirementsResult collectRequirements(PatchContext sharedCtx)'''
    mismatches = []
    requirements = Requirements()
    patchInfo = PatchInfo()

    # pre-check to see if we can get the machine id during update
    if not os.path.exists(GATEWAY_UUID_FILE):
        try:
            subprocess.check_output(MACHINE_ID_CMD.split())
        except Exception as e: #pylint: disable=W0703
            logger.error("Failed to get machine id with error %s", str(e))
            mismatch = Mismatch(INTERNAL_ERROR_TEXT,
                       description=INTERNAL_ERROR_TEXT,
                       resolution=INTERNAL_ERROR_RESOLUTION,
                       severity=Mismatch.ERROR)
            mismatches.append(mismatch)
            return RequirementsResult(mismatches=mismatches)

    return RequirementsResult(requirements, patchInfo, mismatches)

@extend(Hook.Validation)
def validate(ctx):  # pylint: disable=W0613
    '''ValidationResult validate(PatchContext sharedCtx)'''
    mismatches = []

    return ValidationResult(mismatches)

@extend(Hook.Prepatch)
def prePatch(ctx):  # pylint: disable=W0613
    '''void prePatch(PatchContext sharedCtx) throw UserUpgradeError'''

    # pre-patch for gateway uuid file, to be created if absent
    # with value as machine id for existing gateways
    if not os.path.exists(GATEWAY_UUID_FILE):
        output = subprocess.check_output(MACHINE_ID_CMD.split())
        gateway_id = output.decode().strip('\n')
        content = "instanceUuid=%s" % gateway_id
        with open(GATEWAY_UUID_FILE, 'w') as uuid_file:
            uuid_file.write(content)
            logger.info('Created appliance.cfg with gateway uuid')


@extend(Hook.Patch)
def patch(ctx):  # pylint: disable=W0613
    '''void patch(PatchContext sharedCtx) throw UserUpgradeError'''

    # patch for products.conf file, mark HLM as installed if firstboots done
    stageDir = os.path.abspath(os.path.join(ctx.stageDirectory,
                                            os.pardir, os.pardir))
    if isHlmInstalled():
        content = {}
        if os.path.exists(PRODUCTS_FILE):
            with open(PRODUCTS_FILE) as fp:
                content = json.load(fp)
            logger.info('products.conf already present')

        content["HLM"] = {"installed": "true"}
        with open(PRODUCTS_FILE, 'w') as outfile:
            json.dump(content, outfile, indent=2)
            logger.info('Marked HLM as installed')

    # patch for decoupled HLM update, we need to create HLM specific
    # update.conf file in existing gateways. we can copy the appliance
    # update.conf and update with entries for HLM. We do this only if
    # the file doesn't already exists and FSS in target build is enabled
    # and HLM is installed
    update_conf_file = os.path.join(HLM_UPDATE_CONF_DIR, 'update.conf')
    if not os.path.exists(update_conf_file) and \
            fss_utils.getTargetFSS(GW_PLATFORM_UPDATE_FSS) \
            and isHlmInstalled():
        if not os.path.isdir(HLM_UPDATE_CONF_DIR):
            os.makedirs(HLM_UPDATE_CONF_DIR, exist_ok=True)
        logger.info('Creating update.conf for HLM')
        with open(os.path.join(stageDir, 'rpm-manifest.json'), "r") as f:
            rpm_manifest_json = json.load(f)
        with open(UPDATE_CONF_FILE, "r") as f:
            update_conf_json = json.load(f)
        # update the conf per HLM
        update_conf_json.pop("latestPatch", None)
        update_conf_json['name'] = 'Patch for Hybrid Linked Mode'
        update_conf_json['product'] = 'HLM'
        # since UPDATE_CONF_FILE is not updated yet, use version from
        # the target build for HLM update.conf
        update_conf_json['version'] = rpm_manifest_json['header']['version']
        update_conf_json['buildnumber'] = \
            rpm_manifest_json['header']['buildnumber']
        update_conf_json['changesetnumber'] = \
            rpm_manifest_json['header'].get('changesetnumber', None)

        # write the conf
        with open(update_conf_file, "w") as f:
            json.dump(update_conf_json, f, sort_keys=True, indent=4)
            logger.info('Created update.conf for HLM')
