# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import argparse
import plistlib
import ssl
import sys
from io import BytesIO
from urllib.request import urlopen
from xml.dom import minidom

import certifi
from mozpack.macpkg import Pbzx, uncpio, unxar


def get_english(dict, default=None):
    english = dict.get("English")
    if english is None:
        english = dict.get("en", default)
    return english


def get_content_at(url):
    ssl_context = ssl.create_default_context(cafile=certifi.where())
    f = urlopen(url, context=ssl_context)
    return f.read()


def get_plist_at(url):
    return plistlib.loads(get_content_at(url))


def show_package_content(url, digest=None, size=None):
    package = get_content_at(url)
    if size is not None and len(package) != size:
        print(f"Package does not match size given in catalog: {url}", file=sys.stderr)
        sys.exit(1)
    # Ideally we'd check the digest, but it's not md5, sha1 or sha256...
    # if digest is not None and hashlib.???(package).hexdigest() != digest:
    #     print(f"Package does not match digest given in catalog: {url}", file=sys.stderr)
    #     sys.exit(1)
    for name, content in unxar(BytesIO(package)):
        if name == "Payload":
            for path, _, __ in uncpio(Pbzx(content)):
                if path:
                    print(path.decode("utf-8"))


def show_product_info(product, package_id=None):
    # An alternative here would be to look at the MetadataURLs in
    # product["Packages"], but going with Distributions allows to
    # only do one request.
    dist = get_english(product.get("Distributions"))
    data = get_content_at(dist)
    dom = minidom.parseString(data.decode("utf-8"))
    for pkg_ref in dom.getElementsByTagName("pkg-ref"):
        if pkg_ref.childNodes:
            if pkg_ref.hasAttribute("packageIdentifier"):
                id = pkg_ref.attributes["packageIdentifier"].value
            else:
                id = pkg_ref.attributes["id"].value

            if package_id and package_id != id:
                continue

            for child in pkg_ref.childNodes:
                if child.nodeType != minidom.Node.TEXT_NODE:
                    continue
                for p in product["Packages"]:
                    if p["URL"].endswith("/" + child.data):
                        if package_id:
                            show_package_content(
                                p["URL"], p.get("Digest"), p.get("Size")
                            )
                        else:
                            print(id, p["URL"])


def show_products(products, filter=None):
    for key, product in products.items():
        metadata_url = product.get("ServerMetadataURL", "")
        if metadata_url and (not filter or filter in metadata_url):
            metadata = get_plist_at(metadata_url)
            localization = get_english(metadata.get("localization", {}), {})
            title = localization.get("title", None)
            version = metadata.get("CFBundleShortVersionString", None)
            print(key, title, version)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--catalog",
        help="URL of the catalog",
        default="https://swscan.apple.com/content/catalogs/others/index-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
    )
    parser.add_argument(
        "--filter", help="Only show entries with metadata url matching the filter"
    )
    parser.add_argument(
        "what", nargs="?", help="Show packages information about the given entry"
    )
    args = parser.parse_args()

    data = get_plist_at(args.catalog)
    products = data["Products"]

    if args.what:
        if args.filter:
            print(
                "Cannot use --filter when showing verbose information about an entry",
                file=sys.stderr,
            )
            sys.exit(1)
        product_id, _, package_id = args.what.partition("/")
        show_product_info(products[product_id], package_id)
    else:
        show_products(products, args.filter)


if __name__ == "__main__":
    main()
