import json

from conda import __version__  # NOQA
from conda.gateways import logging  # NOQA
from conda.base.context import context
from conda.auxlib.decorators import clear_memoized_methods
from conda.models.channel import prioritize_channels, Channel
from conda.models.version import VersionOrder  # NOQA
from conda.models.match_spec import MatchSpec, ExactStrMatch  # NOQA
from conda.gateways.connection.download import download  # NOQA
from conda.gateways.connection.session import CondaSession

try:
    from conda.core.subdir_data import fetch_repodata_remote_request, SubdirData
except Exception:
    fetch_repodata_remote_request = None
    from conda.core.subdir_data import SubdirData
from conda.exceptions import UnavailableInvalidChannel


_known_subdirs = None


def known_subdirs():
    global _known_subdirs
    if _known_subdirs is None:
        _known_subdirs = sorted(set(context.known_subdirs) - {"noarch"}) + ["noarch"]
    return _known_subdirs


def channel_alias():
    return context.channel_alias.base_url.rstrip("/") + "/"


def default_channels():
    return [x.base_url for x in context.default_channels]


def empty_repodata(subdir):
    return {
        "info": {"subdir": subdir},
        "packages": {},
        "packages.conda": {},
        "removed": [],
        "repodata_version": 1,
    }


_repo_tokens = None


def get_repo_tokens():
    global _repo_tokens
    if _repo_tokens is None:
        from conda.gateways import anaconda_client as ac

        _repo_tokens = {}
        for url, token in ac.read_binstar_tokens().items():
            url = "/".join(url.split("/", 3)[:3]) + "/"
            _repo_tokens[url] = token
    return _repo_tokens


def set_repo_token(url, token):
    global _repo_tokens
    if _repo_tokens is None:
        get_repo_tokens()
    url = "/".join(url.split("/", 3)[:3]) + "/"
    if token:
        _repo_tokens[url] = token
    elif url in _repo_tokens:
        del _repo_tokens[url]


def find_token(url):
    url = url.rstrip("/") + "/"
    for t_url, token in get_repo_tokens().items():
        if url.startswith(t_url):
            return token


def apply_repo_token(url):
    token = find_token(url)
    if token:
        c = Channel(url)
        c.token = token
        return c.url(True)
    return url


def _cache_clear():
    # Unfortunately conda<24 does not implement CondaSession.cache_clear.
    # This implements effectively backports the equivalent functionality
    # to older versions of conda
    if hasattr(CondaSession, "cache_clear"):
        CondaSession.cache_clear()
    elif hasattr(CondaSession._thread_local, "sessions"):
        CondaSession._thread_local.sessions.clear()
    elif hasattr(CondaSession._thread_local, "session"):
        delattr(CondaSession.thread_local, "session")


def fetch_index(channel_urls, platform, channel_alias=None, ssl_verify=None):
    try:
        context.repodata_use_zst = False
    except Exception:
        pass
    if channel_alias is not None and channel_alias != context.channel_alias:
        old_alias, context._channel_alias = context._channel_alias, channel_alias
        clear_memoized_methods(context, "channel_alias")
        Channel._reset_state()
    else:
        channel_alias = None
    if ssl_verify is not None and ssl_verify != context.ssl_verify:
        old_ssl, context.ssl_verify = context.ssl_verify, ssl_verify
        _cache_clear()
    else:
        ssl_verify = None
    if not isinstance(channel_urls, (list, tuple)):
        channel_urls = (channel_urls,)
    try:
        filtered_results = []
        for url in prioritize_channels(
            channel_urls, with_credentials=False, subdirs=(platform,)
        ):
            if url not in filtered_results and url.rstrip("/").endswith("/" + platform):
                filtered_results.append(apply_repo_token(url))
        index = empty_repodata(platform)
        for url in reversed(filtered_results):
            if hasattr(SubdirData, "repo_fetch"):
                repodata, _ = SubdirData(Channel(url)).repo_fetch.fetch_latest_parsed()
            else:
                try:
                    raw_repodata_str = fetch_repodata_remote_request(url, None, None)
                except UnavailableInvalidChannel:
                    continue
                if raw_repodata_str is None:
                    continue
                repodata = json.loads(raw_repodata_str)
            if not isinstance(repodata, dict):
                continue
            for key in ("packages", "packages.conda"):
                for fn, pkg in repodata.get(key, {}).items():
                    index[key][fn] = pkg
                    pkg["subdir"] = platform
                    pkg["channel"] = url
                    pkg["fn"] = fn
        return index
    finally:
        if channel_alias is not None:
            context._channel_alias = old_alias
            clear_memoized_methods(context, "channel_alias")
        if ssl_verify is not None:
            context.ssl_verify = old_ssl
            _cache_clear()
