import sys

from urllib.parse import urlparse

from .common import _api
from .utils import MirrorException, fp_or_bytes


def get_repo_api(config):
    dest_site = config.dest_site
    if not dest_site:
        raise MirrorException("No Artifactory repository specified.")
    scheme, netloc, path, *remains = urlparse(dest_site)
    if path and not (scheme or netloc or any(remains)):
        if "/" in path:
            netloc, path = path.split("/", 1)
        else:
            netloc, path = path, "/artifactory"
    if not netloc:
        raise MirrorException("Invalid dest_site: %s" % dest_site)
    path = path.strip("/")
    if path in ("", "artifactory"):
        path = "artifactory"
    elif not path.startswith("artifactory/"):
        raise MirrorException("Path must start with /artifactory: %s" % dest_site)
    base = tuple(path.split("/")[1:])
    dest_site = (scheme or "https") + "://" + netloc + "/artifactory"
    config.dest_site = dest_site
    config.api_url = dest_site + "/api"
    return base


def begin_channel(base, cname, dry_run):
    repo = base[0] if base else cname.split("/", 1)[0]
    response = _api("get", "repositories", repo)
    if response["rclass"] != "local":
        raise MirrorException("Must be a local repository: %s" % repo)


def list_objects(base, cname, platform=None, repodata=None):
    # Also grab information a listing of the actual files
    localdata = _api("get", "storage", *base, cname, platform, accept_404=True)
    packages = {}
    repodata = repodata or {}
    s1, s2 = repodata.get("packages", {}), repodata.get("packages.conda", {})
    for frec in localdata.get("children", ()):
        if frec["folder"]:
            continue
        fn = frec["uri"].lstrip("/")
        prec = packages[fn] = {}
        # Let's not waste an API call to get the checksums for packages that
        # are in the index. Instead we just trust the index. If we could do a
        # bulk listing, as we can with other destinations like Nexus or S3,
        # it would be worth the effort. That said, creating the empty dictionary
        # above is important, because it tells our code that the file is there.
        if fn not in s1 and fn not in s2:
            data = _api("get", "storage", *base, cname, platform, fn)
            prec["size"] = int(data["size"])
            prec["md5"] = data["checksums"]["md5"]
            prec["sha1"] = data["checksums"]["sha1"]
            prec["sha256"] = data["checksums"]["sha256"]
    return packages


def get_object(base, *path):
    try:
        return _api("get", "..", *base, *path)
    except MirrorException as exc:
        if " 404 " not in exc.message:
            raise


def del_object(base, *path, metadata=None):
    _api("delete", "..", *base, *path, accept_404=True)


def put_object(base, *path, path_or_data=None, metadata=None):
    headers = {
        "X-Checksum-" + csum: metadata[csum.lower()]
        for csum in ("Sha256", "Sha1", "MD5")
        if metadata and csum.lower() in metadata
    }
    with fp_or_bytes(path_or_data) as data:
        _api(
            "put",
            "..",
            *base,
            *path,
            headers=headers,
            data=data,
        )


def main():
    from .common import main as _main

    _main(sys.modules[__name__])
