#!/usr/bin/python3 -u

# Copyright 2022 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 argparse
import os
import subprocess
import sys
import tempfile
import time
from typing import List

SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
PROJECT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", ".."))


def wait_for_server(port: int, delay: float = 0.1) -> float:
    cmd = ["nc", "-z", "-w", "1", "127.0.0.1", str(port)]
    while subprocess.call(cmd) != 0:
        time.sleep(0.01)
    time.sleep(delay)


def execute_tester(
    binary: str,
    queries: List[str],
    should_fail: bool = False,
    failure_message: str = "",
    username: str = "",
    password: str = "",
    check_failure: bool = True,
) -> None:
    args = [binary, "--username", username, "--password", password]
    if should_fail:
        args.append("--should-fail")
    if failure_message:
        args.extend(["--failure-message", failure_message])
    if check_failure:
        args.append("--check-failure")
    args.extend(queries)
    subprocess.run(args).check_returncode()


def execute_flag_check(binary: str, queries: List[str], expected: int, username: str = "", password: str = "") -> None:
    args = [binary, "--username", username, "--password", password]

    args.extend(queries)
    args.append(str(expected))

    subprocess.run(args).check_returncode()


def start_memgraph(memgraph_args: List[any]) -> subprocess:
    memgraph = subprocess.Popen(list(map(str, memgraph_args)))
    time.sleep(0.1)
    assert memgraph.poll() is None, "Memgraph process died prematurely!"
    wait_for_server(7687)

    return memgraph


def execute_with_user(tester_binary: str, queries: List[str]) -> None:
    return execute_tester(
        tester_binary, queries, should_fail=False, check_failure=True, username="admin", password="admin"
    )


def METHOD_NAME(
    tester_binary: str,
    queries: List[str],
    should_fail: bool = False,
    failure_message: str = "",
    check_failure: bool = True,
) -> None:
    return execute_tester(tester_binary, queries, should_fail, failure_message, "", "", check_failure)


def cleanup(memgraph: subprocess):
    if memgraph.poll() is None:
        memgraph.terminate()
    assert memgraph.wait() == 0, "Memgraph process didn't exit cleanly!"


def test_without_any_files(tester_binary: str, memgraph_args: List[str]):
    memgraph = start_memgraph(memgraph_args)
    METHOD_NAME(tester_binary, ["MATCH (n) RETURN n"], False)
    cleanup(memgraph)


def test_init_file(tester_binary: str, memgraph_args: List[str]):
    memgraph = start_memgraph(memgraph_args)
    execute_with_user(tester_binary, ["MATCH (n) RETURN n"])
    METHOD_NAME(tester_binary, ["MATCH (n) RETURN n"], True, "Handshake with the server failed!", True)
    cleanup(memgraph)


def test_init_data_file(flag_checker_binary: str, memgraph_args: List[str]):
    memgraph = start_memgraph(memgraph_args)
    execute_flag_check(flag_checker_binary, ["MATCH (n) RETURN n"], 2, "user", "user")
    cleanup(memgraph)


def test_init_and_init_data_file(flag_checker_binary: str, tester_binary: str, memgraph_args: List[str]):
    memgraph = start_memgraph(memgraph_args)
    execute_with_user(tester_binary, ["MATCH (n) RETURN n"])
    METHOD_NAME(tester_binary, ["MATCH (n) RETURN n"], True, "Handshake with the server failed!", True)
    execute_flag_check(flag_checker_binary, ["MATCH (n) RETURN n"], 2, "user", "user")
    cleanup(memgraph)


def execute_test(memgraph_binary: str, tester_binary: str, flag_checker_binary: str) -> None:
    storage_directory = tempfile.TemporaryDirectory()
    memgraph_args = [memgraph_binary, "--data-directory", storage_directory.name]

    # Start the memgraph binary
    with open(os.path.join(os.getcwd(), "dummy_init_file.cypherl"), "w") as temp_file:
        temp_file.write("CREATE USER admin IDENTIFIED BY 'admin';\n")
        temp_file.write("CREATE USER user IDENTIFIED BY 'user';\n")

    with open(os.path.join(os.getcwd(), "dummy_init_data_file.cypherl"), "w") as temp_file:
        temp_file.write("CREATE (n:RANDOM) RETURN n;\n")
        temp_file.write("CREATE (n:RANDOM {name:'1'}) RETURN n;\n")

    # Run the test with all combinations of permissions
    print("\033[1;36m~~ Starting env variable check test ~~\033[0m")
    test_without_any_files(tester_binary, memgraph_args)
    memgraph_args_with_init_file = memgraph_args + [
        "--init-file",
        os.path.join(os.getcwd(), "dummy_init_file.cypherl"),
    ]
    test_init_file(tester_binary, memgraph_args_with_init_file)
    memgraph_args_with_init_data_file = memgraph_args + [
        "--init-data-file",
        os.path.join(os.getcwd(), "dummy_init_data_file.cypherl"),
    ]

    test_init_data_file(flag_checker_binary, memgraph_args_with_init_data_file)
    memgraph_args_with_init_file_and_init_data_file = memgraph_args + [
        "--init-file",
        os.path.join(os.getcwd(), "dummy_init_file.cypherl"),
        "--init-data-file",
        os.path.join(os.getcwd(), "dummy_init_data_file.cypherl"),
    ]
    test_init_and_init_data_file(flag_checker_binary, tester_binary, memgraph_args_with_init_file_and_init_data_file)
    print("\033[1;36m~~ Ended env variable check test ~~\033[0m")

    os.remove(os.path.join(os.getcwd(), "dummy_init_data_file.cypherl"))
    os.remove(os.path.join(os.getcwd(), "dummy_init_file.cypherl"))


if __name__ == "__main__":
    memgraph_binary = os.path.join(PROJECT_DIR, "build", "memgraph")
    tester_binary = os.path.join(PROJECT_DIR, "build", "tests", "integration", "flag_check", "tester")
    flag_checker_binary = os.path.join(PROJECT_DIR, "build", "tests", "integration", "flag_check", "flag_check")

    parser = argparse.ArgumentParser()
    parser.add_argument("--memgraph", default=memgraph_binary)
    parser.add_argument("--tester", default=tester_binary)
    parser.add_argument("--flag_checker", default=flag_checker_binary)
    args = parser.parse_args()

    execute_test(args.memgraph, args.tester, args.flag_checker)

    sys.exit(0)