#!/usr/bin/env python3

from asyncio import run
from tabulate import tabulate
from argparse import ArgumentParser

from rougail.utils import normalize_family
from tiramisu.error import PropertiesOptionError
from risotto.machine import load, remove_cache, ROUGAIL_NAMESPACE


HIDE_SECRET = True


def list_to_string(lst):
    if isinstance(lst, list):
        return "\n".join([str(val) for val in lst])
    return lst


async def get_files_subelements(type_name, element, files_subelement, files_cols):
    data = {}
    if not await element.option('activate').value.get():
        return data
    for subelement in files_subelement.values():
        if subelement['type'] == 'subelement':
            try:
                value = list_to_string(await element.option(subelement['key']).value.get())
            # FIXME except AttributeError:
            except Exception:
                value = ''
        elif subelement['type'] == 'information':
            value = await element.information.get(subelement['key'], '')
        elif subelement['type'] == 'none':
            value = subelement['value']
        else:
            raise Exception('unknown subelement')
        if value != '':
            files_cols.add(subelement['key'])
        data[subelement['key']] = value
    if type_name == 'overrides':
        data['name'] = f'/systemd/system/{data["source"]}.d/rougail.conf'
        if not data['engine']:
            data['engine'] = 'none'
    elif not data['engine']:
        data['engine'] = 'cheetah'
    return data


async def services(config, values):
    files_subelement = {'Source': {'key': 'source', 'type': 'information'},
                        'Nom': {'key': 'name', 'type': 'subelement'},
                        'Variable': {'key': 'variable', 'type': 'subelement'},
                        'Propriétaire': {'key': 'owner', 'type': 'subelement'},
                        'Groupe': {'key': 'group', 'type': 'subelement'},
                        'Mode': {'key': 'mode', 'type': 'subelement'},
                        'Moteur': {'key': 'engine', 'type': 'information'},
                        }
    disabled_services = []
    for service in await config.option.list(type="all"):
        doc = await service.option.doc()
        files_lst = []
        files_cols = set()
        if not await service.option('manage').value.get():
            doc += " - unmanaged"
        if not await service.option('activate').value.get():
            disabled_services.append([doc])
        else:
            for type in await service.list(type="all"):
                type_name = await type.option.doc()
                if type_name in ['files', 'overrides']:
                    for element in await type.list(type="all"):
                        data = await get_files_subelements(type_name, element, files_subelement, files_cols)
                        if data:
                            files_lst.append(data)
                elif type_name == 'manage':
                    pass
                elif type_name == 'activate':
                    if not await type.value.get():
                        doc += " - unactivated"
                else:
                    print("FIXME " + type_name)
            if files_lst:
                keys = [key for key, val in files_subelement.items() if val['key'] in files_cols]
                values[doc] = {'keys': keys, 'lst': []}
                for lst in files_lst:
                    values[doc]['lst'].append([val for key, val in lst.items() if key in files_cols])
    if disabled_services:
        values["Services désactivés"] = {'keys': ['Nom'], 'lst': disabled_services}


async def table_leader(config, read_only):
    keys = ['Description']
    if read_only:
        keys.append('Cachée')
    leadership_lst = await config.list(type="all")
    leader = leadership_lst.pop(0)
    leader_owner = await leader.owner.get()
    follower_names = [await follower.option.name() for follower in leadership_lst]
    doc = await leader.option.doc()
    properties = await leader.property.get()
    if 'mandatory' in properties:
        doc += '*'
    name = await leader.option.name()
    lst = [[f'{doc} ({name})']]
    if read_only:
        if 'hidden' in properties:
            hidden = 'oui'
        else:
            hidden = ''
        lst[0].append(hidden)
    for idx, leader_value in enumerate(await leader.value.get()):
        keys.append(f'Valeur {idx}')
        keys.append(f'Utilisateur {idx}')
        lst[0].append(leader_value)
        lst[0].append(leader_owner)
        for follower_idx, follower_name in enumerate(follower_names):
            follower_option = config.option(follower_name, idx)
            if idx == 0:
                doc = await follower_option.option.doc()
                properties = await follower_option.property.get()
                if 'mandatory' in properties:
                    doc += '*'
                name = await follower_option.option.name()
                lst.append([f'{doc} ({name})'])
                if read_only:
                    if 'hidden' in properties:
                        hidden = 'oui'
                    else:
                        hidden = ''
                    lst[-1].append(hidden)
            try:
                lst[follower_idx + 1].append(list_to_string(await follower_option.value.get()))
                lst[follower_idx + 1].append(await follower_option.owner.get())
            except PropertiesOptionError:
                pass
#    leader = next leader_iter
#        if master_values is None:
#            master_values = await subconfig.value.get()
    return {'keys': keys, 'lst': lst}


async def table(config, prefix_len, values, read_only):
    lst = []
    for subconfig in await config.option.list(type="all"):
#        prefix = prefix_len * 2 * ' '
#        if await subconfig.option.isoptiondescription():
#            prefix += '=>'
#        else:
#            prefix += '-'
#        display_str = f'{prefix} {description}'
#        if name != description:
#            display_str = f'{display_str} ({name})'
        name = await subconfig.option.name()
        doc = await subconfig.option.doc()
        if prefix_len == 0 and ROUGAIL_NAMESPACE != name:
            doc = doc.capitalize()
        if prefix_len == 0 and name == 'services':
            values['Services'] = {}
            await services(subconfig, values['Services'])
        elif await subconfig.option.isoptiondescription():
            od_name = f'{doc} ({(await subconfig.option.path()).split(".", 1)[1]})'
            values[od_name] = None
            if await subconfig.option.isleadership():
                values[od_name] = await table_leader(subconfig, read_only)
            else:
                values[od_name] = await table(subconfig, prefix_len + 1, values, read_only)
        else:
            value = list_to_string(await subconfig.value.get())
            doc = await subconfig.option.doc()
            properties = await subconfig.property.get()
            if 'mandatory' in properties:
                doc += '*'
            name = await subconfig.option.name()
            lst.append([f'{doc} ({name})', value])
            if read_only:
                if 'hidden' in properties:
                    hidden = 'oui'
                else:
                    hidden = ''
                lst[-1].append(hidden)
            lst[-1].append(await subconfig.owner.get())
    keys = ['Description', 'Valeur']
    if read_only:
        keys.append('Cachée')
    keys.append('Utilisateur')
    return {'keys': keys, 'lst': lst}


async def main():
    parser = ArgumentParser()
    parser.add_argument('server_name')
    parser.add_argument('--read_only', action='store_true')
    parser.add_argument('--nocache', action='store_true')
    parser.add_argument('--debug', action='store_true')
    args = parser.parse_args()
    if args.nocache:
        remove_cache()

    values = {}
    server_name = args.server_name
    config = await load(hide_secret=HIDE_SECRET,
                        original_display_name=True,
                        valid_mandatories=args.read_only,
                        )
    if not args.read_only:
        await config.property.read_write()
    root_option = config.option(normalize_family(server_name))
    try:
        await root_option.option.get()
    except AttributeError:
        exit(f'Unable to find {server_name} configuration: {[await o.option.description() for o in await config.option.list(type="optiondescription")]}')
    await table(root_option, 0, values, args.read_only)
    for title, dico in values.items():
        if title == 'Services':
            if not dico:
                continue
            print()
            print(title)
            print('=' * len(title))
            print()
            for subtitle, dic in dico.items():
                print()
                print('   ' + subtitle)
                print('   ' + '-' * len(subtitle))
                print()
                print(tabulate(dic['lst'], headers=dic['keys'], tablefmt="fancy_grid"))
        elif dico['lst']:
            print()
            print(title)
            print('=' * len(title))
            print()
            print(tabulate(dico['lst'], headers=dico['keys'], tablefmt="fancy_grid"))


run(main())