"""One native manifest/feed writer for admitted previews and protected releases."""
from __future__ import annotations

import base64
import hashlib
from pathlib import Path


from hermes_cli.release_channels import ChannelError, build_prefix, canonical_json, validate_manifest
from scripts.bundles.release_artifacts import single, validate_windows_bundle, write_appinstaller


def assemble(request: dict, native: dict, root: Path, *, artifact_prefix: str) -> tuple[dict, list[Path]]:
    files = native["files"]
    rows = native["packages"]
    if sorted((row["platform"], row["arch"]) for row in rows) != [
            (platform, arch) for platform in ("macos", "windows") for arch in ("arm64", "x64")]:
        raise ChannelError("Native metadata must cover both platforms and architectures")
    prefix = build_prefix(request["buildId"])
    base = request["publicBase"]
    windows = [row for row in rows if row["platform"] == "windows"]
    if windows[0]["applicationId"] != request["identity"]["appNamePascal"]:
        raise ChannelError("Windows application identity mismatch")
    bundle_name = single(name for name in files if name.endswith(".msixbundle") and not name.startswith("Store-"))
    validate_windows_bundle(root / bundle_name, windows)
    packages, mac_files = [], []
    for row in rows:
        mac = row["platform"] == "macos"
        filename = row["filename"] if mac else bundle_name
        receipt = files.get(filename)
        if receipt is None or not (root / filename).is_file():
            raise ChannelError("Missing receipt-bound native artifact")
        signing = "teamId" if mac else "publisher"
        packages.append({"platform": "darwin" if mac else "win32", "arch": row["arch"], "variant": "bundled",
                         "identity": row["identity"], "version": row["version"], signing: row.get(signing),
                         "artifact": {"key": artifact_prefix + filename, "sha256": receipt["sha256"], "size": receipt["size"]},
                         "feed": {"key": prefix + ("darwin/stable-mac.yml" if mac else "win32/stable.appinstaller"),
                                  "channel": "stable"}})
        if mac:
            for name in (filename, filename.removesuffix(".zip") + ".dmg"):
                if any(item not in files or not (root / item).is_file() for item in (name, name + ".blockmap")):
                    raise ChannelError("Missing receipt-bound macOS package or blockmap")
                with (root / name).open("rb") as stream:
                    digest = base64.b64encode(hashlib.file_digest(stream, "sha512").digest()).decode("ascii")
                mac_files.append({"url": f"{base}/{artifact_prefix}{name}", "sha512": digest, "size": files[name]["size"]})
    manifest = {"schema": 1, "request": request, "packages": packages}
    if all(row.get("receiverProtocol") == 1 for row in rows):
        manifest["receiverProtocol"] = 1
    tag = request.get("releaseTag", "")
    from hermes_cli.update_channel import is_canary_tag
    record = {"name": request["channel"], "repository": request["repository"], "identity": request["identity"],
              "policy": ("canary-release" if is_canary_tag(tag) else "stable-release") if tag else "preview", "head": None}
    validate_manifest(manifest, record, base)
    descriptor = root / "win32" / "stable.appinstaller"
    write_appinstaller(descriptor, identity=windows[0]["identity"], publisher=windows[0]["publisher"],
                       version=windows[0]["version"], self_uri=f"{base}/{prefix}win32/stable.appinstaller",
                       artifact_uri=f"{base}/{artifact_prefix}{bundle_name}", update_policy="pinned")
    feed = root / "darwin" / "stable-mac.yml"
    feed.parent.mkdir(parents=True, exist_ok=True)
    first = single(row for row in mac_files if row["url"].endswith("-arm64.zip"))
    # A neutral feed name avoids interpreting custom channel names as prereleases.
    feed.write_bytes(canonical_json({"version": request["version"], "files": mac_files,
                                    "path": first["url"], "sha512": first["sha512"]}))
    return manifest, [feed, descriptor]
