import os
from threading import Event

from aldpro_core.rabbit2psql.amqp import AMQPTransport
from aldpro_core.rabbit2psql.full_sync.full_sync import FullSync
from aldpro_core.rabbit2psql.full_sync.full_sync_logger import full_sync_logger
from aldpro_core.rabbit2psql.orm import ORM
from aldpro_core.rabbit2psql.orm.mapping import APPENDED_MAPPING
from aldpro_core.rabbit2psql.settings import FULL_SYNC_LOC_LOCATION, ALIVE_LOC_LOCATION
from aldpro_core.rabbit2psql.utils import (  # create_appended_member,
    comapre_kwargs,
    create_appended_dict,
    create_appended_globals,
    get_table_from_dn,
    normalize_dict_keys,
)

orm = ORM()

event = Event()


def prepare_appended_dict(message_args: dict) -> dict:
    appended_dict = dict()

    if message_args:
        message_args = normalize_dict_keys(message_args)

        for key in APPENDED_MAPPING.keys():
            item_value = message_args.get(key)

            if item_value:
                if key in ("objectclass", "memberof", "member"):
                    appended_dict.update(create_appended_globals(orm, key, item_value))
                # elif key == "member": # TODO
                #     appended_dict.update(create_appended_member(orm, item_value))
                else:
                    appended_dict.update(create_appended_dict(orm, key, item_value))

    return appended_dict


def process_modify_chunk(chunk: dict, dn: str):
    message = dict()
    arg = dict()
    for key, value in chunk.get("arg").items():
        if value:
            arg[key] = value
        else:
            arg[key] = None

    message["dn"] = dn
    message["op_type"] = "modify"
    message["arg"] = arg

    process_message(message)


def process_message(message: dict):
    dn = message.get("dn", "")
    arg = message.get("arg")
    op_type = message.get("op_type")
    table_name = get_table_from_dn(dn)

    filter_key = "dn"
    if "cn=computers," in dn:
        filter_key = "fqdn"

    if op_type == "FULLSYNC":
        event.set()
        sync = FullSync()
        sync.full_sync()
        event.clear()
    elif op_type == "ALIVE":
        with open(ALIVE_LOC_LOCATION, "w") as file:
            file.write("")
    elif op_type == "add":
        appended = prepare_appended_dict(arg)
        kwargs = comapre_kwargs(orm, dn, arg)
        kwargs.update({"dn": dn})
        orm.create(table_name, appended, **kwargs)
    elif op_type == "modify":
        appended = prepare_appended_dict(arg)
        kwargs = comapre_kwargs(orm, dn, arg)
        kwargs.update({"dn": dn})
        orm.update(table_name, {filter_key: dn}, appended, **kwargs)
    elif op_type == "delete":
        orm.delete(table_name, {filter_key: dn})


if __name__ == "__main__":
    transport = AMQPTransport()
    transport.read_with_delay(event)

    try:
        if not os.path.exists(FULL_SYNC_LOC_LOCATION):
            with open(FULL_SYNC_LOC_LOCATION, "w") as f:
                f.write("")
            process_message({"op_type": "FULLSYNC"})
        modify_chunk = dict()
        while True:
            chunk = transport.get_chunk()
            for message_id, message in enumerate(chunk):
                first_key = [key for key in message.keys()][0]

                if first_key.isdigit():
                    dn = message[first_key].get("dn")
                    fqdn = message[first_key].get("fqdn")

                    dn = dn if dn else fqdn
                    del fqdn

                    modify_chunk_dn = modify_chunk.get(dn)
                    if modify_chunk_dn:
                        if int(first_key) in modify_chunk_dn.get("digits"):
                            process_modify_chunk(modify_chunk_dn, dn)
                            modify_chunk_dn["digits"].clear()
                            modify_chunk_dn["arg"] = dict()

                        if not transport.inspect_next_digit_exist(int(first_key), dn, chunk[message_id + 1 :]):
                            modify_chunk_dn["digits"].append(int(first_key))
                            modify_chunk_dn["arg"].update(message[first_key].get("arg"))

                            process_modify_chunk(modify_chunk_dn, dn)
                            del modify_chunk[dn]
                            continue

                        modify_chunk_dn["digits"].append(int(first_key))
                        modify_chunk_dn["arg"].update(message[first_key].get("arg"))
                    else:
                        modify_chunk.update(
                            {
                                dn: {
                                    "digits": [int(first_key)],
                                    "arg": message[first_key].get("arg"),
                                }
                            }
                        )
                    continue
                process_message(message)
    except Exception as e:
        if os.path.exists(FULL_SYNC_LOC_LOCATION):
            os.unlink(FULL_SYNC_LOC_LOCATION)
        full_sync_logger.fatal(e)
        raise Exception(e)
    finally:
        transport.stop_reading()
        transport.channel.close()
        transport.connection.close()
        del orm
