# Copyright 2021 Memgraph Ltd.
#
# Use of this software is governed by the Business Source License
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
# License, and you may not use this file except in compliance with the Business Source License.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0, included in the file
# licenses/APL.txt.

import logging
from argparse import ArgumentParser
from common import get_absolute_path, APOLLO
from databases import Memgraph, Neo
from clients import QueryClient, LongRunningClient

log = logging.getLogger(__name__)


class LongRunningSuite:
    KNOWN_KEYS = {"config", "setup", "run"}
    headers = ["elapsed_time", "num_executed_queries"]

    def __init__(self, args):
        argp = ArgumentParser("LongRunningSuiteArgumentParser")
        argp.add_argument("--duration", type=int)
        self.args, _ = argp.parse_known_args(args)
        pass

    def run(self, scenario, group_name, scenario_name, runner):
        runner.start()

        log.info("Executing setup")
        runner.setup(scenario.get("setup")())

        config = next(scenario.get("config")())
        duration = config["duration"]
        if self.args.duration:
            duration = self.args.duration
        log.info("Executing run for {} seconds".format(
                duration))
        results = runner.run(next(scenario.get("run")()), duration, config["client"])

        runner.stop()

        measurements = []
        summary_format = "{:>15} {:>22} {:>22}\n"
        self.summary = summary_format.format(
                "elapsed_time", "num_executed_queries", "num_executed_steps")
        for result in results:
            self.summary += summary_format.format(
                    result["elapsed_time"], result["num_executed_queries"],
                    result["num_executed_steps"])
            measurements.append({
                "target": "throughput",
                "time": result["elapsed_time"],
                "value": result["num_executed_queries"],
                "steps": result["num_executed_steps"],
                "unit": "number of executed queries",
                "type": "throughput"})
        self.summary += "\n\nThroughput: " + str(measurements[-1]["value"])
        self.summary += "\nExecuted steps: " + str(measurements[-1]["steps"])
        return measurements

    def runners(self):
        return {"MemgraphRunner": MemgraphRunner, "NeoRunner": NeoRunner}

    def groups(self):
        return ["pokec", "card_fraud", "bfs_pokec"]


class _LongRunningRunner:
    def __init__(self, args, database, num_client_workers, workload):
        self.log = logging.getLogger("_LongRunningRunner")
        self.database = database
        self.query_client = QueryClient(args, num_client_workers)
        self.long_running_client = LongRunningClient(args, num_client_workers,
                                                     workload)

    def start(self):
        self.database.start()

    def setup(self, queries, num_client_workers=None):
        return self.query_client(queries, self.database, num_client_workers)

    def run(self, config, duration, client, num_client_workers=None):
        return self.long_running_client(
            config, self.database, duration, client, num_client_workers)

    def stop(self):
        self.log.info("stop")
        self.database.stop()


# TODO: This is mostly copy pasted from MemgraphParallelRunner from
# query_suite.py. Think of the way to remove duplication.
class MemgraphRunner(_LongRunningRunner):
    """
    Configures memgraph database for LongRunningSuite execution.
    """
    def __init__(self, args):
        argp = ArgumentParser("MemgraphRunnerArgumentParser")
        argp.add_argument("--num-database-workers", type=int, default=8,
                          help="Number of workers")
        argp.add_argument("--num-client-workers", type=int, default=24,
                          help="Number of clients")
        argp.add_argument("--workload", type=str, default="",
                          help="Type of client workload. Sets \
                          scenario flag for 'TestClient'")
        self.args, remaining_args = argp.parse_known_args(args)
        assert not APOLLO or self.args.num_database_workers, \
            "--num-database-workers is obligatory flag on apollo"
        assert not APOLLO or self.args.num_client_workers, \
            "--num-client-workers is obligatory flag on apollo"
        database = Memgraph(remaining_args, self.args.num_database_workers)
        super(MemgraphRunner, self).__init__(
                remaining_args, database, self.args.num_client_workers,
                self.args.workload)


class NeoRunner(_LongRunningRunner):
    """
    Configures neo4j database for QuerySuite execution.
    """
    def __init__(self, args):
        argp = ArgumentParser("NeoRunnerArgumentParser")
        argp.add_argument("--runner-config",
                          default=get_absolute_path("config/neo4j.conf"),
                          help="Path to neo config file")
        argp.add_argument("--num-client-workers", type=int, default=24,
                          help="Number of clients")
        argp.add_argument("--workload", type=str, default="",
                          help="Type of client workload. Sets \
                          scenario flag for 'TestClient'")
        self.args, remaining_args = argp.parse_known_args(args)
        assert not APOLLO or self.args.num_client_workers, \
            "--client-num-clients is obligatory flag on apollo"
        database = Neo(remaining_args, self.args.runner_config)
        super(NeoRunner, self).__init__(
                remaining_args, database, self.args.num_client_workers,
                self.args.workload)
