#!/usr/bin/env python3

# Script builds the conan package using the Aurora SDK.
#
# Script is meant to be run inside the build environment
# that has conan installed.
#
# In order to publish build packages you must be
# authenticated in the remote.
#

import argparse
import json
from os.path import abspath, dirname, isfile, join
import subprocess
import sys
from typing import Dict, List
from common import args
from common import aurora
from common import constants


REQUIREMENTS_FILE = "buildrequirements.txt"


def main():
    arguments = parse_arguments()
    recepie_path = abspath(arguments.recepie)
    version = arguments.version

    if not aurora.check_sb2():
        print(
            "This script should be run inside the Aurora Build Engine "
            + "or in Aurora Platform SDK",
            file=sys.stderr,
        )
        return 1

    targets = aurora.find_targets()
    if len(targets) == 0:
        print("No targets found", file=sys.stderr)
        return 1

    targets = aurora.filter_targets(targets, arguments.target)
    if len(targets) == 0:
        print("No targets matching target filter found")
        return 1

    any_target = aurora.pick_target(targets)

    package_info = get_conan_package_info(recepie_path, any_target)
    if len(package_info) == 0:
        print("Could not determine conan package information", file=sys.stderr)
        return 1

    requirements = read_package_build_requirements(recepie_path)

    if arguments.build and not build_all_conan_packages(
        recepie_path,
        package_info["name"],
        version,
        requirements,
        arguments.clear,
        targets,
    ):
        print("Unable to build the package", file=sys.stderr)
        return 1

    package_id = f"{package_info['name']}/{version}@{package_info['user']}"
    if arguments.publish and not publish_conan_package(package_id, targets):
        print("Unable to publish the package", file=sys.stderr)
        return 1

    print("All operations have finished")
    return 0


def parse_arguments() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Build and publish conan packages")
    args.add_recepie_argument(parser)
    args.add_version_argument(parser)
    args.add_build_option(parser)
    args.add_clear_option(parser)
    args.add_publish_option(parser)
    args.add_target_option(parser)
    return parser.parse_args()


def get_conan_package_info(recepie_path: str, target: str) -> dict:
    print("Retrieving package info")
    try:
        result = subprocess.run(
            [
                *aurora.target_prefix(target),
                aurora.CONAN_EXEC_NAME,
                "inspect",
                "--format",
                "json",
                recepie_path,
            ],
            capture_output=True,
            check=True,
        )
        return json.loads(result.stdout)
    except subprocess.CalledProcessError as error:
        print("Could not retrieve a conan package info", file=sys.stderr)
        print(error.output, file=sys.stderr)
        return {}


def read_package_build_requirements(recepie_path: str) -> list:
    requirements_file = join(recepie_path, REQUIREMENTS_FILE)
    if not isfile(requirements_file):
        return []
    with open(requirements_file, "r") as file:
        return file.read().splitlines()


def build_all_conan_packages(
    recepie_path: str,
    package_name: str,
    version: str,
    requirements: List[str],
    clear: bool,
    targets: Dict[str, str],
) -> bool:
    for architecture in targets:
        target = targets[architecture]
        if not (
            install_requirements(requirements, target)
            and build_conan_package(recepie_path, package_name, version, clear, target)
        ):
            return False
    return True


def install_requirements(requirements: List[str], target: str) -> bool:
    if len(requirements) == 0:
        return True
    print(f"Installing requirements in target {target}")
    zypper_command = [*aurora.target_prefix(target), "-R", "zypper", "-n"]
    try:
        subprocess.run(
            [*zypper_command, "refresh"],
            check=True,
        )
        subprocess.run([*zypper_command, "install", "-y", *requirements])
    except subprocess.CalledProcessError as error:
        print("Could not install requirements")
        return False
    return True


def get_build_flags(target):
    try:
        raw_flags = subprocess.run(
            [*target_prefix(target), "rpm", "--eval", "%{set_build_flags}"],
            check=True, capture_output=True
        )
        return raw_flags.stdout.decode("utf-8").strip()
    except subprocess.CalledProcessError as error:
        print("Could not get build flags", file=sys.stderr)
        print(error.output, file=sys.stderr)
        return None


def build_conan_package(
    recepie_path: str, package_name: str, version: str, clear: bool, target: str
) -> bool:
    if clear and not aurora.run_conan_command(
        "remove local package",
        target,
        ["remove", "--confirm", f"{package_name}/{version}"],
    ):
        return False
    return aurora.run_conan_command(
        "build package",
        target,
        ["create", "--version", version, recepie_path],
    )


def publish_conan_package(
    package_id: str,
    targets: Dict[str, str],
) -> bool:
    any_target = aurora.pick_target(targets)
    remote_package_present = aurora.run_conan_command(
        "check remote package",
        any_target,
        ["remote", "list", "-r", constants.CONAN_REPOSITORY_ID, package_id],
        show_error=False,
    )
    if remote_package_present and not aurora.run_conan_command(
        "remove remote package",
        any_target,
        ["remove", "--confirm", "-r", constants.CONAN_REPOSITORY_ID, package_id],
    ):
        return False
    for architecture in targets:
        target = targets[architecture]
        if not publish_conan_package_in_target(package_id, target):
            return False
    return True


def publish_conan_package_in_target(package_id: str, target: str) -> bool:
    return aurora.run_conan_command(
        f"upload package",
        target,
        ["upload", "-r", constants.CONAN_REPOSITORY_ID, package_id],
    )


if __name__ == "__main__":
    sys.exit(main())
