#!/usr/bin/env python3

import sys
import os
import shutil
import subprocess
import multiprocessing
import json
import stat
import filecmp
import urllib.request
from argparse import ArgumentParser


def mkdir(path):
    try:
        os.mkdir(path)
    except FileExistsError as e:
        pass


def remove_file(path):
    try:
        os.remove(path)
    except FileNotFoundError as e:
        pass


def compare_files(lhs_file_path, rhs_file_path):
    try:
        return filecmp.cmp(lhs_file_path, rhs_file_path)
    except FileNotFoundError as e:
        return False


def rmtree(path):
    try:
        shutil.rmtree(path)
    except FileNotFoundError as e:
        pass


def generate_graph_for_platform(generate_graph_for_platform):
    platform = generate_graph_for_platform[0]
    generate_graph_command = generate_graph_for_platform[1]

    output = subprocess.check_output(
        generate_graph_command, stderr=subprocess.STDOUT, shell=True
    ).decode("utf-8")

    allowed_error_patterns = [
        "to directory without ya.make: [[imp]]$S/build/platform/",
        "to missing directory: [[imp]]$S/build/platform/",
        "to directory without ya.make: [[imp]]$S/build/external_resources/",
        "to missing directory: [[imp]]$S/build/external_resources/",
        "could not resolve include file: [[imp]]openssl",
        "could not resolve include file: [[imp]]zlib",
        "could not resolve include file: [[imp]]ares.h",
        "in $B/contrib/libs/openssl/",
        "in $B/contrib/libs/zlib",
        "in $B/contrib/libs/c-ares",
        "in $B/contrib/libs/libc_compat/ubuntu_14/liblibs-libc_compat-ubuntu_14.a",
        "in $B/contrib/libs/linux-headers/libcontrib-libs-linux-headers.a",
        "in $B/contrib/libs/farmhash/",
        "in $B/contrib/libs/curl/",
        "in $B/contrib/libs/libxml/",
        "in $B/contrib/libs/apache/arrow/",
        "in $B/contrib/libs/grpc/",
        "in $S/contrib/tools/protoc/plugins/cpp_styleguide/ya.make",
        "in $S/contrib/tools/protoc/plugins/grpc_cpp",
        "in $B/contrib/restricted/boost/",
        "in $B/library/cpp/charset/",
        "in $B/library/cpp/uri/",
        "in $B/library/cpp/unicode/punycode/",
        "in $B/library/cpp/config/",
        "in $S/tools/rescompiler/bin/",
        # Fix
        "in $B/ydb/library/actors/dnsresolver/ut/library-cpp-actors-dnsresolver-ut",
        "in $B/ydb/library/pdisk_io/libydb-library-pdisk_io",
        "skip unknown statement: ADD_YTEST vector of size",
        "skip unknown statement: _REGISTER_NO_CHECK_IMPORTS vector of size",
        "skip unknown statement: CHECK_CONTRIB_CREDITS vector of size",
        "skip unknown statement: ASSERT vector of size",
        "skip unknown statement: FILES vector of size",
        "skip unknown statement: ADD_CHECK vector of size",
        "skip unknown statement: COPY vector of size",
        "skip unknown statement: PY_EXTRALIBS vector of size",
        "skip unknown statement: LLVM_BC vector of size",
        "skip unknown statement: SUPPRESSIONS vector of size",
        ". It is not intended for export.",
    ]

    if platform == "windows-x86_64":
        # Fix
        allowed_error_patterns.append("in $B/ydb/core/tx/tiering/core-tx-tiering")
        allowed_error_patterns.append(
            "in $B/ydb/library/yql/providers/s3/serializations/providers-s3-serializations"
        )

    result_errors = []
    for line in output.split("\n"):
        if not line.startswith("Error"):
            continue

        error_is_allowed = False
        for allowed_error_pattern in allowed_error_patterns:
            if allowed_error_pattern in line:
                error_is_allowed = True
                break

        if error_is_allowed:
            continue

        result_errors.append(line)

    return result_errors


if __name__ == "__main__":
    parser = ArgumentParser(description="Generate CMake files from Ya make files")
    parser.add_argument("--ymake_bin", help="Path to ymake binary")
    parser.add_argument("--yexport_bin", help="Path to yexport binary")
    parser.add_argument("--tmp", help="Path to tmp dir")
    parser.add_argument(
        "-k", "--keep-going", action="store_true", default=False, help="Ignore unknown build graph errors and try to perform export")
    parser.add_argument(
        "--debug", action="store_true", default=False, help="Run script in debug mode"
    )

    try:
        args = parser.parse_args()
    except Exception as e:
        print(e, file=sys.stderr)
        sys.exit(1)

    tmp_folder_path = args.tmp
    if tmp_folder_path is None:
        tmp_folder_path = "/tmp/ydb-generate-cmake"

    ymake_binary_path = args.ymake_bin
    yexport_binary_path = args.yexport_bin
    debug = args.debug
    keep_going = args.keep_going
    root_folder = os.getcwd()

    ydb_tmp_folder_path = tmp_folder_path + "/ydb"
    ydb_metadata_folder_path = tmp_folder_path + "/metadata"
    plugins_folder_path = root_folder + "/build/plugins"

    mkdir(tmp_folder_path)
    mkdir(ydb_metadata_folder_path)

    if ymake_binary_path is None:
        ymake_binary_path = root_folder + "/ya tool ymake"

    if yexport_binary_path is None:
        # libiconv_path="contrib/libs/libiconv/dynamic"
        # compile_libiconv_command = f"{root_folder}/ya make -r {libiconv_path}"
        # print("Compliling libiconv...")
        # subprocess.check_output(compile_libiconv_command, shell=True)
        # yexport_binary_path = f"LD_LIBRARY_PATH={libiconv_path} {root_folder}/ya tool yexport"
        yexport_binary_path = f"{root_folder}/ya tool yexport"

    platforms = [
        "linux-x86_64",
        "linux-aarch64",
        "darwin-x86_64",
        "darwin-arm64",
        "windows-x86_64",
    ]

    generate_graph_for_platform_commands = []

    for platform in platforms:
        target_platform = "default-" + platform
        print(f"Platform {platform} target platform {target_platform}")

        dump_export_path = f"{ydb_metadata_folder_path}/{platform}.conf"
        graph_export_path = f"{ydb_metadata_folder_path}/sem.{platform}.json"

        generate_dump_command = f"{root_folder}/scripts/generate_dump.sh {platform} {target_platform} > {dump_export_path}"
        print(f"Generate dump command {generate_dump_command}")

        subprocess.check_output(generate_dump_command, shell=True)

        # In original script there are targets kikimr/docs/ru/docs_oss ydb ydb/tests/oss/launch library/cpp/actors tools/rescompiler/bin
        generate_graph_command = f'{ymake_binary_path} --build-root "{ydb_tmp_folder_path}" --config "{dump_export_path}"\
            --plugins-root "{plugins_folder_path}" --xs --xx --sem-graph --keep-going\
            ydb ydb/tests/oss/launch tools/rescompiler/bin > {graph_export_path}'
        print(f"Generate graph command {generate_graph_command}")

        generate_graph_for_platform_commands.append((platform, generate_graph_command))

    errors_for_platform = []
    with multiprocessing.Pool(len(generate_graph_for_platform_commands)) as pool:
        errors_for_platform = pool.map(
            generate_graph_for_platform, generate_graph_for_platform_commands
        )

    yexport_args = [yexport_binary_path, "--export-root", f"\"{root_folder}\"", "--target", "YDB", "--generator", "cmake"]

    for index, platform in enumerate(platforms):
        errors_for_platform_size = len(errors_for_platform[index])

        if errors_for_platform_size == 0:
            yexport_args += [
                "--semantic-graph",
                f"\"{ydb_metadata_folder_path + '/sem.' + platform + '.json'}\"",
                "--platforms",
                platform,
            ]
            continue

        print(
            f"Found {errors_for_platform_size} errors for platform {platform}",
            file=sys.stderr,
        )
        for error in errors_for_platform[index]:
            print(error, file=sys.stderr)

        if not keep_going:
            sys.exit(1)

    yexport_command = ' '.join(yexport_args)

    print(f"yexport command {yexport_command}")

    yexport_output = subprocess.check_output(
        yexport_command, stderr=subprocess.STDOUT, shell=True
    ).decode("utf-8")

    if debug:
        print("yexport output")
        print(yexport_output)

    sys.exit(0)

    rsync_command = f'rsync --recursive --delete  --perms\
        --exclude .git --exclude contrib --exclude library/cpp/actors\
        "{ydb_tmp_folder_path}/" "{root_folder}/"'

    print(f"rsync command {rsync_command}")
    subprocess.check_output(rsync_command, shell=True)

    sys.exit(0)