# Copyright (c) 2021-2022 VMware, Inc. All rights reserved.
# VMware Confidential

import os
import logging
import sqlite3
from update_utils import runCommand
from os.path import join, abspath, dirname
import json

logger = logging.getLogger(__name__)
PATCH_DB_PATH = "/storage/db/patching.db"
DEPLOYMENT_ROOT = abspath(join(dirname(__file__), os.pardir))
RPM_MANIFEST_FILE = join(DEPLOYMENT_ROOT, 'rpm-manifest.json')
PRE_INSTALL_RPM_LIST = join(DEPLOYMENT_ROOT, 'scripts', 'preinstall-rpm-list.json')

def execute(db, sql):
    '''
    Attempts to execute given SQL statement provided the db connection arg
    is valid and maintains connection to the database. In case the given db
    connection is invalid, the API will throw exception that the caller must
    catch and re-issue the call with validate db connection object.
    The function can be used for all CRUD operations.
    '''

    try:
        logger.debug("Executing {%s}", sql)
        cursor = db.cursor()
        cursor.execute(sql)
        res = cursor.fetchall()
        db.commit()

    except Exception as de:
        logger.exception("Failed to execute query: {%s}", sql)

    return res

def openDb(dbPath):
    '''
    Opens a sqlite DB at the given dbPath arg
    Throws exception in case of errors.
    '''
    db = None

    try:
        logger.info("Opening connection to database")
        db = sqlite3.connect(dbPath)
        return db

    except Exception as e:
        logger.exception("Could not open connection to database %s", dbPath)

def checkAndInstallRpms(stage_dir):
    '''
    Checks if the rpms in preinstall-rpm-list are present or not, and installs it separately,
    removes the rpm from database as well as from stage directory
    '''
    list_rpms = list(os.listdir(stage_dir))
    with open(PRE_INSTALL_RPM_LIST) as f:
        pre_install_rpm_list = json.load(f)
    with open(RPM_MANIFEST_FILE) as f:
        rpm_manifest_list = json.load(f)['files']
    for rpm_group in pre_install_rpm_list:
        global gDb
        if rpm_group["rpm_name"] in rpm_manifest_list and rpm_manifest_list[rpm_group["rpm_name"]]["relativepath"] in list_rpms:
            rpms_to_install = [rpm_manifest_list[dependency]["relativepath"] for dependency in rpm_group["dependencies"]
                               if dependency in rpm_manifest_list and rpm_manifest_list[dependency]["relativepath"] in list_rpms]
            rpms_to_install.append(rpm_manifest_list[rpm_group["rpm_name"]]["relativepath"])
            rpms_with_path = [stage_dir + "/" + rpm for rpm in rpms_to_install]
            logger.info("Installing rpms : %s", rpms_to_install)
            out, err, rc = runCommand(["rpm", "-Uvh", *rpms_with_path], progress=True, message="Installing rpms {}".format(rpms_to_install))
            if rc:
                logger.error("Installation of %s rpms failed: Error: %s, Output: %s", rpms_to_install, err, out)
                return
            if os.path.isfile(PATCH_DB_PATH):
                gDb = openDb(PATCH_DB_PATH)
                for rpm in rpms_to_install:
                    query = "DELETE from meta where _name='{}'".format(rpm)
                    execute(gDb, query)
            # Removing rpms from stage directory
            out1, err1, rc1 = runCommand(["rm", *rpms_with_path], progress=True,
                                         message="Removing from stage directory %s rpms.".format(rpms_to_install))
            if rc1:
                logger.error("Removal of %s rpm from stage directory failed: Error: %s, Output: %s", rpms_to_install,
                             err1, out1)
