import os
import errno
import sys
import tarfile
import time
import requests
import json

from library.python.testing import recipe
from library.python.testing.recipe import declare_recipe, set_env
from library.python.testing.recipe.ports import get_port_range, release_port_range
import yatest.common

PID_FILE = "intranet_d_recipe.pid"
CLASSPATH_DIR_NAME = "intranet-d-classpath"
SPRING_PROFILE = "test-recipe"
PROCESS_CHECK_ATTEMPTS = 30
WAIT_SECONDS_BETWEEN_ATTEMPTS = 10


def get_env(key):
    try:
        fobj_cm = open(recipe.ya.env_file)
    except OSError as e:
        if e.errno == errno.ENOENT:
            return None
        else:
            raise
    else:
        with fobj_cm as fobj:
            for line in fobj:
                line_data = json.loads(line)
                value = line_data.get(key)
                if value is not None:
                    return value
    return None


def http_check(port):
    try:
        res = requests.get("http://localhost:{}/local/readiness".format(port))
        return res.status_code == 200
    except requests.RequestException:
        return False


def is_running(pid):
    try:
        os.kill(pid, 0)
    except OSError as err:
        if err.errno == errno.ESRCH:
            return False
    return True


def start(argv):
    port = get_port_range(port_count=2)
    set_env("RECIPE_HTTP_PORT", str(port))
    set_env("RECIPE_GRPC_PORT", str(port + 1))
    sub_recipie_args = [yatest.common.build_path("kikimr/public/tools/ydb_recipe/ydb_recipe")] + sys.argv[1:] + ["--use-packages", "kikimr/public/tools/package/stable"]
    yatest.common.execute(sub_recipie_args)
    ydb_endpoint = get_env("YDB_ENDPOINT")
    ydb_database = get_env("YDB_DATABASE")
    env = dict(
        RECIPE_HTTP_PORT=str(port),
        RECIPE_GRPC_PORT=str(port + 1),
        YDB_ENDPOINT=ydb_endpoint,
        YDB_DATABASE=ydb_database,
        RECIPE_LOGS_DIR=yatest.common.output_path("")
    )
    java_bin = yatest.common.runtime.global_resources()['JDK17_RESOURCE_GLOBAL'] + '/bin/java'
    main_class = "ru.yandex.intranet.d.DApplication"
    tar_file_dir = yatest.common.binary_path("intranet/d/backend/service/app/src/main")
    tar_path = tar_file_dir + '/d-backend.tar'
    os.makedirs(CLASSPATH_DIR_NAME)
    tarfile.open(tar_path).extractall(CLASSPATH_DIR_NAME)
    with open(yatest.common.output_path("intranet-d-backend-startup.log"), "wt") as logfile:
        print("Launching java process...")
        execution = yatest.common.execute(
            [java_bin,
                "-Dfile.encoding=UTF-8",
                "-Djava.net.preferIPv6Addresses=true",
                "-Dspring.profiles.active=" + SPRING_PROFILE,
                "-Dlog4j.configurationFile=log4j2-d-recipe-config.xml",
                "-Dio.netty.leakDetection.level=paranoid",
                "-Dio.netty.leakDetection.targetRecords=50",
                "-XX:+DisableExplicitGC",
                "-XX:ErrorFile=" + yatest.common.output_path("intranet_d_backend_hs_err_pid%p.log"),
                "-XX:+HeapDumpOnOutOfMemoryError",
                "-XX:+ExitOnOutOfMemoryError",
                "-XX:HeapDumpPath=" + yatest.common.output_path(""),
                "-Xlog:all=warning:file=" + yatest.common.output_path("intranet-d-backend-jvm.log") + ":time,level,tags:filecount=10,filesize=10m",
                "-Xlog:safepoint=info,gc*=info,gc+ref*=debug,gc+ergo*=trace:file=" + yatest.common.output_path("intranet-d-backend-gc.log") + ":time,level,tags:filecount=10,filesize=10m",
                "-cp", CLASSPATH_DIR_NAME + "/*",
                main_class],
            env=env,
            stdout=logfile,
            stderr=logfile,
            wait=False
        )
        print("Waiting for java process initialization...")
        exit_code = execution.process.poll()
        if exit_code is not None:
            raise RuntimeError("Java process failed with exit code " + str(exit_code))
        initialized = False
        for n in range(PROCESS_CHECK_ATTEMPTS):
            exit_code = execution.process.poll()
            if exit_code is not None:
                raise RuntimeError("Java process failed with exit code " + str(exit_code))
            if http_check(port):
                initialized = True
                break
            time.sleep(WAIT_SECONDS_BETWEEN_ATTEMPTS)
        exit_code = execution.process.poll()
        if exit_code is not None:
            raise RuntimeError("Java process failed with exit code " + str(exit_code))
        with open(PID_FILE, "w") as f:
            f.write(str(execution.process.pid))
        if not initialized:
            raise RuntimeError("Java procress failed to initialize in time")
        print("Java process successfully initialized with pid " + str(execution.process.pid))


def stop(argv):
    try:
        with open(PID_FILE) as f:
            pid = int(f.read())
            print("Stopping java process with pid " + str(pid))
            try:
                os.kill(pid, 15)
            except:
                pass
            for n in range(30):
                if not is_running(pid):
                    break
                time.sleep(1)
            if is_running(pid):
                try:
                    os.kill(pid, 9)
                except:
                    pass
    except:
        pass
    print("Finished stopping java process")
    release_port_range()
    sub_recipie_args = [yatest.common.build_path("kikimr/public/tools/ydb_recipe/ydb_recipe")] + sys.argv[1:] + ["--use-packages", "kikimr/public/tools/package/stable"]
    yatest.common.execute(sub_recipie_args)


if __name__ == "__main__":
    declare_recipe(start, stop)
