# coding=utf-8
"""
Exports data from optparse or argparse based manage.py commands and reports it to _xml.XmlDumper.
This module encapsulates Django semi-public API knowledge, and not very stable because of it.
Optional env var ``_PYCHARM_DJANGO_DEFAULT_TIMEOUT`` sets timeout (in seconds) to wait for each command to be
fetched.
"""
import sys
import threading
from _jb_utils import VersionAgnosticUtils
from django.core.management import ManagementUtility, get_commands
from django_manage_commands_provider._parser import _optparse, _argparse
import os

__author__ = 'Ilya.Kazakevich'


class _Fetcher(threading.Thread):
    def __init__(self, utility, command_name):
        super(_Fetcher, self).__init__()
        self.result = None
        self.__utility = utility
        self.__command_name = command_name
        self.command_lead_to_exception = False

    def run(self):
        try:
            self.result = self.__utility.fetch_command(self.__command_name)
        except Exception as e:
            sys.stderr.write("Error fetching command '{0}': {1}\n".format(self.__command_name, e))
            self.command_lead_to_exception = True


def report_data(dumper, commands_to_skip):
    """
    Fetches data from management commands and reports it to dumper.

    :type dumper _xml.XmlDumper
    :type commands_to_skip list
    :param commands_to_skip list of commands to skip
    :param dumper: destination to report
    """
    utility = ManagementUtility()
    for command_name in get_commands().keys():

        if command_name in commands_to_skip:
            sys.stderr.write("Skipping command '{0}' due to config\n".format(command_name))
            continue

        fetcher = _Fetcher(utility, command_name)
        fetcher.daemon = True
        fetcher.start()
        fetcher.join(int(os.getenv("_PYCHARM_DJANGO_DEFAULT_TIMEOUT", "2")))
        command = fetcher.result
        if not command:
            if fetcher.command_lead_to_exception:
                sys.stderr.write("Command '{0}' skipped\n".format(command_name))
                continue
            else:
                sys.stderr.write(
                    "Command '{0}' took too long and may freeze everything. Consider adding it to 'skip commands' list\n".format(
                        command_name))
                sys.exit(1)

        use_argparse = False
        # There is no optparse in 1.10
        if _is_django_10():
            use_argparse = True
        else:
            try:
                use_argparse = command.use_argparse
            except AttributeError:
                pass

        try:
            parser = command.create_parser("", command_name)
        except Exception as e:
            sys.stderr.write("Error parsing command {0}: {1}\n".format(command_name, e))
            continue

        try:  # and there is no "usage()" since 1.10
            usage = command.usage("")
        except AttributeError:
            usage = command.help

        dumper.start_command(command_name=command_name,
                             command_help_text=VersionAgnosticUtils().to_unicode(usage).replace("%prog",
                                                                                                command_name))
        module_to_use = _argparse if use_argparse else _optparse  # Choose appropriate module: argparse, optparse
        module_to_use.process_command(dumper, command, parser)
        dumper.close_command()


def _is_django_10():
    """

    :return: is Django >= 1.10
    """
    try:
        import django
    except ImportError:
        return False
    try:
        django_version = django.get_version()
    except AttributeError:
        return False
    try:
        from distutils.version import StrictVersion, LooseVersion
        try:
            return StrictVersion(django_version) >= StrictVersion("1.10")
        except ValueError:
            return LooseVersion(django_version) >= LooseVersion("1.10")
    except ImportError:
        if sys.version_info[:2] >= (3, 12):
            return django_version.split('.') >= ['1', '10']
        return False
