"""
Synchronize Kontor data between configured servers.
"""

import json
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
from logging import Logger
from typing import Dict, List

from api import (
    MAPPING,
    EndPointNotAvailableException,
    Server,
    get_api_config,
    get_logger,
)

parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("--verbose", "-v", action="count", default=0)
parser.add_argument("--config", "-c", default="kontor-api")
parser.add_argument("--dry-run", "-m", action="store_true")
parser.add_argument("--to-file", "-f", action="store_true")
parser.add_argument("--server", "-s")
parser.add_argument("--cleanup", "-d", action="store_true")
args = parser.parse_args()


def create_item_id_mapping(log: Logger, data_list: List[dict]) -> Dict[str, dict]:
    """
    create dictionary with id as key and dictionary as value.
    """
    item_id_mapping: Dict[str, dict] = {}
    for data_item in data_list:
        log.debug(data_item)
        item_id_mapping[data_item["id"]] = data_item
    return item_id_mapping


def is_different(log: Logger, first_item, second_item: dict) -> bool:
    """
    Check dicts for differences and returns true if values are not equals, except for last_modified_date.
    """
    check_result = False
    for key, value in first_item.items():
        if key in second_item.keys():
            if value != second_item[key]:
                log.info("%s: %s != %s", key, value, second_item[key])
                if key == "last_modified_date":
                    continue
                if not check_result:
                    check_result = True
    return check_result


if __name__ == "__main__":
    logger = get_logger(args.verbose, "kontor")
    logger.info("kontor.sync started")
    APICONFIG = get_api_config(logger, args.config)
    server_list: List[Server] = []
    if args.server:
        server = APICONFIG.get_server(args.server)
        if server:
            server_list.append(server)
    else:
        server_list.extend(APICONFIG.server)
    export_data = {}
    for server in server_list:
        export_data[server.name] = {}
        for table, path in MAPPING.items():
            try:
                data = server.request(logger, table=table)
                logger.info("%s: %s", table, len(data))
                export_data[server.name][table] = data
                logger.info("%s: %s exported", table, len(data))
            except EndPointNotAvailableException:
                logger.info("Endpoint not implemented")
        if args.to_file:
            try:
                json_dump = json.dumps(export_data[server.name], indent=4)
                file_name = f"{server.name}-data.json"
                with open(file_name, "w", encoding="utf-8") as dump_file:
                    dump_file.write(json_dump)
            except TypeError as error:
                logger.info(error)
    for server in server_list:
        logger.info(
            "%s: %s tables exported", server.name, len(export_data[server.name])
        )
    if len(server_list) > 1:
        for table, path in MAPPING.items():
            mapping = create_item_id_mapping(
                logger, export_data[server_list[1].name][table]
            )
            for item in export_data[server_list[0].name][table]:
                logger.debug("checking  %s:%s", table, item["id"])
                check_item_id = item["id"]
                if check_item_id in mapping:
                    check_item = mapping[check_item_id]
                    if is_different(logger, item, check_item):
                        logger.info(
                            "checking values for %s != %s", item["id"], check_item["id"]
                        )
                        logger.debug("diff: %s\n%s", item, check_item)
                        if not args.dry_run:
                            result = server_list[1].update(
                                logger, table, check_item_id, item
                            )
                            logger.info("update result: %s", result)
                    else:
                        logger.debug(
                            "no changes for: %s(%s - %s)",
                            table,
                            item["id"],
                            check_item["id"],
                        )
                else:
                    logger.info(
                        "item %s in %s missing: ", check_item_id, server_list[1].name
                    )
                    if not args.dry_run:
                        server_list[1].create(logger, table, item)
            logger.info("synchronization of %s finished", table)
        logger.info("all tables synchronized")
    else:
        logger.info("not enough server configured for sync")
    logger.info("kontor.sync finished")
