import sys

from collections import namedtuple
from urllib.parse import urlparse

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

from requests_toolbelt.multipart.encoder import MultipartEncoder


NexusApi = namedtuple("NexusApi", ["base", "components"])


def get_repo_api(config):
    dest_site = config.dest_site
    if not dest_site:
        raise MirrorException("No Nexus 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, ""
    if not netloc:
        raise MirrorException("Invalid destination site: %s" % dest_site)
    path = "/" + path.lstrip("/")
    if path == "/":
        path = "/repository"
    if not path.startswith("/repository"):
        msg = "Invalid destination site: %s" % dest_site
        msg += "\nPath must begin with /repository."
        raise MirrorException(msg)
    base = tuple(path.strip("/").split("/"))[1:]
    base_url = (scheme or "https") + "://" + netloc
    config.api_url = base_url + "/service/rest/v1"
    return NexusApi(base, {})


def _join(api, *args):
    args = tuple(a for a in args if a)
    return "/".join(api.base + args).split("/", 1)


def begin_channel(api, cname, dry_run):
    repo, path = _join(api, cname)
    _api("get", "repositories", "raw", "hosted", repo)
    cnt = ""
    while True:
        data = _api("get", "components?repository=" + repo + cnt)
        for irec in data.get("items", []):
            item = irec["assets"][0]
            group, fn = item["path"].rsplit("/", 1)
            grec = api.components.setdefault(group, {})
            grec[fn] = item["checksum"]
            grec[fn]["id"] = irec["id"]
            grec[fn]["url"] = "/" + item["downloadUrl"].split("/", 3)[-1]
        cnt = data.get("continuationToken")
        if not cnt:
            break
        cnt = "&continuationToken=" + data["continuationToken"]


def list_objects(api, *path, repodata=None):
    repo, path = _join(api, *path)
    return api.components.get(path, {})


def get_object(api, *path):
    fn = path[-1]
    repo, path = _join(api, *path[:-1])
    if fn in api.components.get(path, {}):
        return _api("get", api.components[path][fn]["url"])


def del_object(api, *path, metadata):
    fn = path[-1]
    packages = list_objects(api, *path[:-1])
    if fn in packages:
        _api("delete", "components", packages[fn]["id"])


def put_object(api, *path, path_or_data=None, metadata=None):
    fn = path[-1]
    repo, path = _join(api, *path[:-1])
    content_data = _content_data(fn)
    content_type = content_data.pop("Content-Type")
    with fp_or_bytes(path_or_data) as data:
        mdata = MultipartEncoder(
            fields={
                "raw.asset1": (fn, data, content_type, content_data),
                "raw.directory": (None, path),
                "raw.asset1.filename": (None, fn),
            }
        )
        _api(
            "post",
            "components?repository=" + repo,
            data=mdata,
            headers={"Content-Type": mdata.content_type},
        )


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

    _main(sys.modules[__name__])
