#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p python3 sequoia-sq
import json
import os
import shutil
import subprocess
import sys
import tempfile
from collections import OrderedDict

DOWNLOADS_BASE_URL = "https://downloads.1password.com"
OP_PGP_KEY_URL = "https://downloads.1password.com/linux/keys/1password.asc"

class Sources(OrderedDict):
    def __init__(self):
        with open("sources.json", "r") as fp:
            self.update(json.load(fp))

    def persist(self):
        with open("sources.json", "w") as fp:
            print(json.dumps(self, indent=2), file=fp)


class PGP:
    def __new__(cls):
        if not hasattr(cls, "_instance"):
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if hasattr(self, "signer_file"):
            return

        self.sq = shutil.which("sq")
        if (self.sq is None):
            raise SystemExit(f"sequoia sq not found")

        self.signer_file, _ = nix_store_prefetch(OP_PGP_KEY_URL)

    def verify(self, sig, tarball):
        args = [
            self.sq,
            "--batch",
            "--home=none",
            "verify",
            f"--signer-file={self.signer_file}",
            f"--signature-file={sig}",
            tarball
        ]

        try:
            subprocess.run(args, check=True, capture_output=True)
        except subprocess.CalledProcessError as cpe:
            print(cpe.stderr, file=sys.stderr)
            raise SystemExit(f"sq error: {cpe.cmd}")


def nix_store_prefetch(url):
    nix = shutil.which("nix")
    cp = subprocess.run(
        [nix, "store", "prefetch-file", "--json", url], check=True, capture_output=True
    )
    out = json.loads(cp.stdout)

    return out["storePath"], out["hash"]


def mk_url(channel, os, version, arch):
    if os == "linux":
        arch_alias = {"x86_64": "x64", "aarch64": "arm64"}[arch]
        path = f"linux/tar/{channel}/{arch}/1password-{version}.{arch_alias}.tar.gz"
    elif os == "darwin":
        path = f"mac/1Password-{version}-{arch}.zip"
    else:
        raise SystemExit(f"update-sources.py: unsupported OS {os}")

    return f"{DOWNLOADS_BASE_URL}/{path}"


def download(channel, os, version, arch):
    url = mk_url(channel, os, version, arch)
    store_path_tarball, hash = nix_store_prefetch(url)

    # Linux release tarballs come with detached PGP signatures.
    if os == "linux":
        store_path_sig, _ = nix_store_prefetch(url + ".sig")
        PGP().verify(store_path_sig, store_path_tarball)

    return url, hash


def main(args):
    """Gets called with args in `channel/os/version` format.

    e.g.:
      update-sources.py stable/linux/8.10.80 beta/linux/8.10.82-12.BETA
    """
    sources = Sources()

    for triplet in args[1:]:
        channel, os, version = triplet.split("/")
        release = sources[channel][os]
        if release["version"] == version:
            continue

        release["version"] = version
        for arch in release["sources"]:
            url, hash = download(channel, os, version, arch)
            release["sources"][arch].update({"url": url, "hash": hash})

    sources.persist()


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