import os
import sys

import requests

import conans
from conans import __version__ as client_version, tools
from conans.client.cmd.create import create
from conans.client.plugin_manager import PluginManager
from conans.client.recorder.action_recorder import ActionRecorder
from conans.client.client_cache import ClientCache
from conans.client.conf import MIN_SERVER_COMPATIBLE_VERSION, ConanClientConfigParser
from conans.client.manager import ConanManager, existing_info_files
from conans.client.migrations import ClientMigrator
from conans.client.output import ConanOutput
from conans.client.profile_loader import read_profile, profile_from_args, \
    read_conaninfo_profile
from conans.client.recorder.search_recorder import SearchRecorder
from conans.client.recorder.upload_recoder import UploadRecorder
from conans.client.remote_manager import RemoteManager
from conans.client.remote_registry import RemoteRegistry
from conans.client.rest.auth_manager import ConanApiAuthManager
from conans.client.rest.rest_client import RestApiClient
from conans.client.rest.conan_requester import ConanRequester
from conans.client.rest.version_checker import VersionCheckerRequester
from conans.client.runner import ConanRunner
from conans.client.store.localdb import LocalDB
from conans.client.cmd.test import PackageTester
from conans.client.userio import UserIO
from conans.errors import ConanException
from conans.model.ref import ConanFileReference
from conans.model.version import Version
from conans.paths import get_conan_user_home, CONANINFO, BUILD_INFO
from conans.util.env_reader import get_env
from conans.util.files import save_files, exception_message_safe, mkdir
from conans.util.log import configure_logger
from conans.util.tracer import log_command, log_exception
from conans.tools import set_global_instances
from conans.client.cmd.uploader import CmdUpload
from conans.client.cmd.profile import cmd_profile_update, cmd_profile_get,\
    cmd_profile_delete_key, cmd_profile_create, cmd_profile_list
from conans.client.cmd.search import Search
from conans.client.cmd.user import users_clean, users_list, user_set
from conans.client.importer import undo_imports
from conans.client.cmd.export import cmd_export, export_alias
from conans.unicode import get_cwd
from conans.client.remover import ConanRemover
from conans.client.cmd.download import download
from conans.model.workspace import Workspace
from conans.client.graph.graph_manager import GraphManager
from conans.client.loader import ConanFileLoader
from conans.client.graph.proxy import ConanProxy
from conans.client.graph.python_requires import ConanPythonRequire
from conans.client.graph.range_resolver import RangeResolver


default_manifest_folder = '.conan_manifests'


def get_request_timeout():
    timeout = os.getenv("CONAN_REQUEST_TIMEOUT")
    try:
        return float(timeout) if timeout is not None else None
    except ValueError:
        raise ConanException("Specify a numeric parameter for 'request_timeout'")


def get_basic_requester(client_cache):
    requester = requests.Session()
    # Manage the verify and the client certificates and setup proxies

    return ConanRequester(requester, client_cache, get_request_timeout())


def api_method(f):
    def wrapper(*args, **kwargs):
        the_self = args[0]
        try:
            curdir = get_cwd()
            log_command(f.__name__, kwargs)
            with tools.environment_append(the_self._client_cache.conan_config.env_vars):
                # Patch the globals in tools
                return f(*args, **kwargs)
        except Exception as exc:
            msg = exception_message_safe(exc)
            try:
                log_exception(exc, msg)
            except:
                pass
            raise
        finally:
            os.chdir(curdir)
    return wrapper


def _make_abs_path(path, cwd=None, default=None):
    """convert 'path' to absolute if necessary (could be already absolute)
    if not defined (empty, or None), will return 'default' one or 'cwd'
    """
    cwd = cwd or get_cwd()
    if not path:
        abs_path = default or cwd
    elif os.path.isabs(path):
        abs_path = path
    else:
        abs_path = os.path.normpath(os.path.join(cwd, path))
    return abs_path


def _get_conanfile_path(path, cwd, py):
    """
    param py= True: Must be .py, False: Must be .txt, None: Try .py, then .txt
    """
    path = _make_abs_path(path, cwd)

    if os.path.isdir(path):  # Can be a folder
        if py:
            path = os.path.join(path, "conanfile.py")
        elif py is False:
            path = os.path.join(path, "conanfile.txt")
        else:
            path_py = os.path.join(path, "conanfile.py")
            if os.path.exists(path_py):
                path = path_py
            else:
                path = os.path.join(path, "conanfile.txt")

    if not os.path.isfile(path):  # Must exist
        raise ConanException("Conanfile not found: %s" % path)

    if py and not path.endswith(".py"):
        raise ConanException("A conanfile.py is needed (not valid conanfile.txt)")

    return path


class ConanAPIV1(object):

    @staticmethod
    def instance_remote_manager(requester, client_cache, user_io, _client_version,
                                min_server_compatible_version, plugin_manager):

        # Verify client version against remotes
        version_checker_req = VersionCheckerRequester(requester, _client_version,
                                                      min_server_compatible_version,