#!/usr/bin/env python3
import sys
import shutil
import subprocess
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED


def failed(msg: str, code: int = 1) -> None:
    print(msg, file=sys.stderr)
    raise SystemExit(code)


def ensure_dir(p: Path, err_msg: str) -> None:
    try:
        p.mkdir(parents=True, exist_ok=True)
    except Exception as e:
        failed(f"{err_msg}: {e}")


def run_git(args: list[str]) -> str:
    try:
        out = subprocess.check_output(["git", *args], stderr=subprocess.STDOUT)
        return out.decode("utf-8", errors="replace").strip()
    except subprocess.CalledProcessError as e:
        failed(f"git failed: {' '.join(args)}\n{e.output.decode('utf-8', errors='replace')}")


def copy_file(src: Path, dst: Path, err_msg: str) -> None:
    if not src.exists():
        failed(f"{err_msg}: missing {src}")
    ensure_dir(dst.parent, f"Error creating folder {dst.parent}")
    try:
        shutil.copy2(src, dst)
    except Exception as e:
        failed(f"{err_msg}: {e}")


def copy_tree(src: Path, dst: Path) -> None:
    if not src.exists():
        failed(f"Missing path: {src}")
    if src.is_dir():
        shutil.copytree(src, dst, dirs_exist_ok=True)
    else:
        ensure_dir(dst.parent, f"Error creating folder {dst.parent}")
        shutil.copy2(src, dst)


def find_macos_dylib(location: Path) -> Path:
    candidate = location / "lib3mf.dylib"
    if candidate.exists():
        if candidate.is_file():
            return candidate
        if candidate.is_dir():
            libs = sorted(candidate.glob("lib3mf*.dylib"))
            for lib in libs:
                if lib.is_file():
                    return lib
    for lib in sorted(location.glob("lib3mf*.dylib")):
        if lib.is_file():
            return lib
    for lib in sorted(location.rglob("lib3mf*.dylib")):
        if lib.is_file():
            return lib
    failed("Unable to find a macOS lib3mf*.dylib binary in the build artifacts.")


def zip_dir_contents(src_dir: Path, zip_path: Path) -> None:
    # Matches: cd $SDKARTIFACT && zip -r ../$OUTFILE ./*
    try:
        zip_path.parent.mkdir(parents=True, exist_ok=True)
        if zip_path.exists():
            zip_path.unlink()

        with ZipFile(zip_path, "w", compression=ZIP_DEFLATED) as z:
            for p in sorted(src_dir.rglob("*")):
                if p.is_file():
                    z.write(p, p.relative_to(src_dir))
    except Exception as e:
        failed(f"Error zipping SDK: {e}")


def main() -> int:
    sdk_artifact = Path("build/lib3mf_sdk")
    sdk_source = Path("SDK")
    location = Path("build")
    outfile = Path("lib3mf_sdk.zip")

    print(f"Clean artifacts-folder {sdk_artifact}")
    if sdk_artifact.exists():
        shutil.rmtree(sdk_artifact)
    ensure_dir(sdk_artifact, "Error creating artifacts-folder")

    print("Copy License, Readme, Examples and Bindings")
    license_src = Path("LICENSE")
    credits_src = sdk_source / "Credits.txt"
    license_dst = sdk_artifact / "License.txt"

    if not license_src.exists():
        failed(f"Missing {license_src}")
    if not credits_src.exists():
        failed(f"Missing {credits_src}")

    try:
        license_dst.write_bytes(license_src.read_bytes() + credits_src.read_bytes())
    except Exception as e:
        failed(f"Error creating {license_dst}: {e}")

    copy_tree(sdk_source / "Readme.md", sdk_artifact / "Readme.md")
    copy_tree(sdk_source / "Examples", sdk_artifact / "Examples")
    copy_tree(Path("Autogenerated/Bindings"), sdk_artifact / "Bindings")

    ensure_dir(sdk_artifact / "Bin", "Error creating Bin-folder")
    ensure_dir(sdk_artifact / "Lib", "Error creating Lib-folder")

    print("Copy binaries")
    copy_file(location / "lib3mf.dll" / "lib3mf.dll",
              sdk_artifact / "Bin" / "lib3mf.dll",
              "Error copying binary")

    copy_file(location / "lib3mf.so" / "lib3mf.so.2",
              sdk_artifact / "Bin" / "lib3mf.so",
              "Error copying binary")

    mac_lib = find_macos_dylib(location)
    copy_file(mac_lib, sdk_artifact / "Bin" / "lib3mf.dylib", "Error copying binary")

    copy_file(location / "lib3mf.lib" / "lib3mf.lib",
              sdk_artifact / "Lib" / "lib3mf.lib",
              "Error copying binary")

    print("Generate version.txt")
    version_txt = sdk_artifact / "version.txt"
    print(f"VERSIONTXT = {version_txt}")

    git_head = run_git(["rev-parse", "HEAD"])
    git_branch = run_git(["rev-parse", "--abbrev-ref", "HEAD"])

    try:
        with open(version_txt, "w", encoding="utf-8") as f:
            f.write("[lib3mf_sdk]\n")
            # keeping bash semantics exactly (it wrote HEAD hash as BuildNumber)
            f.write(f"BuildNumber = {git_head}\n")
            f.write(f"GITBranch = {git_branch}\n")
            f.write(f"GITRevision = {git_head}\n")
    except Exception as e:
        failed(f"Error writing {version_txt}: {e}")

    print("Zip SDK artifacts")
    # bash zips to build/lib3mf_sdk.zip (because it cd's into SDKARTIFACT)
    zip_path = sdk_artifact.parent / outfile
    zip_dir_contents(sdk_artifact, zip_path)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
