#!/opt/dosis2/bin/python3-venv

import os
import glob
import sys
import uuid
import xml.etree.ElementTree as ET


DEFAULT_CONFIG = '/opt/dosis2/etc/dosisConfig.xml'
CUSTOM_CONFIG = '/opt/dosis2/etc/custom/dosisConfig.xml'
OLD_CUSTOM_CONFIGS_TO_KEEP = 3


# take the property names from the config
# and normalize them into a format more appropriate for
# a dictionary i.e. "Scanner Comm Port" should become "scanner_comm_port"
def normalize_name(property_name):
    return property_name.lower().replace(' ', '_')


# rename the old custom config and tack on a unique id
# incase you need to revert to an older config
def rename_old_custom_config(old_custom_config_path):
    unique_id = str(uuid.uuid4())[:8]
    directory_path, file_name = os.path.split(old_custom_config_path)
    only_the_file_name, extension = os.path.splitext(file_name)
    new_custom_config_path = f"{directory_path}/{only_the_file_name}_{unique_id}{extension}"
    os.rename(old_custom_config_path, new_custom_config_path)


# we only want to keep so many historical copies of dosisConfig.xml
def cull_old_custom_configs():
    custom_dir = '/opt/dosis2/etc/custom'
    pattern = 'dosisConfig_*.xml'
    old_custom_configs = glob.glob(os.path.join(custom_dir, pattern))
    old_custom_configs.sort(key=lambda x: os.path.getmtime(x))
    if len(old_custom_configs) > OLD_CUSTOM_CONFIGS_TO_KEEP:
        files_to_delete = old_custom_configs[:len(old_custom_configs) - OLD_CUSTOM_CONFIGS_TO_KEEP]
        for f in files_to_delete:
            os.remove(f)


# read the given dosisConfig.xml and store in a dictionary.
def read_dosis_config(path_to_dosis_config):
    data = {}
    try:
        dosis_config_tree = ET.parse(path_to_dosis_config)
        root = dosis_config_tree.getroot()
        for section_element in root.iter('section'):
            section_name = section_element.get('name')
            for property_element in section_element.iter('property'):
                normalized_property_name = normalize_name(property_element.get('name'))
                property_value = property_element.find('value').text
                normalized_section_name = normalize_name(section_name)
                if normalized_section_name not in data:
                    data[normalized_section_name] = {}

                data[normalized_section_name][normalized_property_name] = property_value

        return data
    except FileNotFoundError:
        raise ValueError(f"{path_to_dosis_config} not found")
    except ET.ParseError:
        raise ValueError(f"{path_to_dosis_config} is not in the correct format")


# leave it alone if it is already in the custom config. If not add it from the default.
def reconcile_dosis_config_data(default_values, custom_values):
    reconciled_data = custom_values
    # iterate over data
    for section_name in default_values:
        # add any mission sections from the default config to the custom config
        if section_name not in reconciled_data:
            reconciled_data[section_name]
        # either add missing properties to their sections and populate with default data
        # or skip if data already existed in the custom config.
        for property_name, property_value in default_values[section_name].items():
            if property_name not in reconciled_data[section_name]:
                reconciled_data[section_name][property_name] = property_value

    return reconciled_data


# simple function to add a newline before EOF
def write_newline(file_name):
    with open(file_name, 'a') as f:
        f.write('\n')


# write new custom/dosisConfig.xml
def write_dosis_config(dosis_config_dict):
    try:
        dosis_config_tree = ET.parse(DEFAULT_CONFIG)
        root = dosis_config_tree.getroot()
        for section_element in root.iter('section'):
            normalized_section_name = normalize_name(section_element.get('name'))
            for property_element in section_element.iter('property'):
                property_name = property_element.get('name')
                normalized_property_name = normalize_name(property_name)
                if normalized_property_name in dosis_config_dict[normalized_section_name]:
                    value_element = property_element.find('value')
                    value_element.text = dosis_config_dict[normalized_section_name][normalized_property_name]
        dosis_config_tree.write(CUSTOM_CONFIG)
        write_newline(CUSTOM_CONFIG)
    except Exception as e:
        print(f"something went wrong writing the new custom config error: {str(e)}")


if __name__ == '__main__':
    # must be run as root
    if not os.geteuid() == 0:
        print('update handler must be run as root!')
        sys.exit(1)

    # get original data
    default_dosis_config_values = read_dosis_config(DEFAULT_CONFIG)
    custom_dosis_config_values = read_dosis_config(CUSTOM_CONFIG)

    # create backup of current custom config
    rename_old_custom_config(CUSTOM_CONFIG)

    # cleanup old custom configs
    cull_old_custom_configs()

    # reconcile data
    reconciled_dosis_config_values = reconcile_dosis_config_data(default_dosis_config_values,
                                                                 custom_dosis_config_values)
    # write new custom config
    write_dosis_config(reconciled_dosis_config_values)
