import imp
import logging
import os
import sys
from optparse import OptionParser, make_option

from django import VERSION as DJANGO_VERSION
from django.conf import ENVIRONMENT_VARIABLE as SETTINGS_ENVIRONMENT_VARIABLE
from django.core.exceptions import ImproperlyConfigured
from django.core.management import base

from .utils import uppercase_attributes, reraise
from .values import Value, setup_value

installed = False

CONFIGURATION_ENVIRONMENT_VARIABLE = 'DJANGO_CONFIGURATION'
CONFIGURATION_ARGUMENT = '--configuration'
CONFIGURATION_ARGUMENT_HELP = ('The name of the configuration class to load, '
                               'e.g. "Development". If this isn\'t provided, '
                               'the  DJANGO_CONFIGURATION environment '
                               'variable will be used.')


configuration_options = (make_option(CONFIGURATION_ARGUMENT,
                                     help=CONFIGURATION_ARGUMENT_HELP),)


def install(check_options=False):
    global installed
    if not installed:
        if DJANGO_VERSION >= (1, 8):
            orig_create_parser = base.BaseCommand.create_parser

            def create_parser(self, prog_name, subcommand):
                parser = orig_create_parser(self, prog_name, subcommand)
                if isinstance(parser, OptionParser):
                    # in case the option_list is set the create_parser
                    # will actually return a OptionParser for backward
                    # compatibility. It uses BaseCommand.use_argparse
                    # to decide that, which checks for the option_list list
                    base.BaseCommand.option_list += configuration_options
                else:
                    # probably argparse, let's not import argparse though
                    parser.add_argument(CONFIGURATION_ARGUMENT,
                                        help=CONFIGURATION_ARGUMENT_HELP)
                return parser

            base.BaseCommand.create_parser = create_parser
        else:
            # add the configuration option to all management commands
            base.BaseCommand.option_list += configuration_options
        importer = ConfigurationImporter(check_options=check_options)
        sys.meta_path.insert(0, importer)
        installed = True


class ConfigurationImporter(object):
    modvar = SETTINGS_ENVIRONMENT_VARIABLE
    namevar = CONFIGURATION_ENVIRONMENT_VARIABLE
    error_msg = ("Configuration cannot be imported, "
                 "environment variable {0} is undefined.")

    def __init__(self, check_options=False):
        self.argv = sys.argv[:]
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)
        handler = logging.StreamHandler()
        self.logger.addHandler(handler)
        if check_options:
            self.check_options()
        self.validate()
        if check_options:
            self.announce()

    def __repr__(self):
        return "<ConfigurationImporter for '{0}.{1}'>".format(self.module,
                                                              self.name)

    @property
    def module(self):
        return os.environ.get(self.modvar)

    @property
    def name(self):
        return os.environ.get(self.namevar)

    def check_options(self):
        # django switched to argparse in version 1.8
        if DJANGO_VERSION >= (1, 8):
            parser = base.CommandParser(None,
                                        usage="%(prog)s subcommand [options] [args]",
                                        add_help=False)
            parser.add_argument('--settings')
            parser.add_argument('--pythonpath')
            parser.add_argument(CONFIGURATION_ARGUMENT,
                                help=CONFIGURATION_ARGUMENT_HELP)

            parser.add_argument('args', nargs='*')  # catch-all
            try:
                options, args = parser.parse_known_args(self.argv[2:])
                if options.configuration:
                    os.environ[self.namevar] = options.configuration
                base.handle_default_options(options)
            except base.CommandError:
                pass  # Ignore any option errors at this point.
        # django < 1.7 did use optparse
        else:
            from django.core.management import LaxOptionParser
            parser = LaxOptionParser(option_list=configuration_options,
                                     add_help_option=False)
            try:
                options, args = parser.parse_args(self.argv)
                if options.configuration:
                    os.environ[self.namevar] = options.configuration
            except:
                pass  # Ignore any option errors at this point.

    def validate(self):
        if self.name is None:
            raise ImproperlyConfigured(self.error_msg.format(self.namevar))
        if self.module is None:
            raise ImproperlyConfigured(self.error_msg.format(self.modvar))

    def announce(self):
        if len(self.argv) > 1:
            from . import __version__
            from django.utils.termcolors import colorize
            # Django >= 1.7 supports hiding the colorization in the shell
            try:
                from django.core.management.color import no_style
            except ImportError:
                no_style = None

            if no_style is not None and '--no-color' in self.argv:
                stylize = no_style()
            else:
                def stylize(text):
                    return colorize(text, fg='green')

            if (self.argv[1] == 'runserver' and
                    os.environ.get('RUN_MAIN') == 'true'):

                message = ("django-configurations version {0}, using "
                           "configuration '{1}'".format(__version__,
                                                        self.name))
                self.logger.debug(stylize(message))

    def find_module(self, fullname, path=None):
        if fullname is not None and fullname == self.module:
            module = fullname.rsplit('.', 1)[-1]
            return ConfigurationLoader(self.name,
                                       imp.find_module(module, path))
        return None


class ConfigurationLoader(object):

    def __init__(self, name, location):
        self.name = name
        self.location = location

    def load_module(self, fullname):
        if fullname in sys.modules:
            mod = sys.modules[fullname]  # pragma: no cover
        else:
            mod = imp.load_module(fullname, *self.location)
        cls_path = '{0}.{1}'.format(mod.__name__, self.name)

        try:
            cls = getattr(mod, self.name)
        except AttributeError as err:  # pragma: no cover
            reraise(err, "Couldn't find configuration '{0}' "
                         "in module '{1}'".format(self.name,
                                                  mod.__package__))
        try:
            cls.pre_setup()
            cls.setup()
            obj = cls()
            attributes = uppercase_attributes(obj).items()
            for name, value in attributes:
                if callable(value) and not getattr(value, 'pristine', False):
                    value = value()
                    # in case a method returns a Value instance we have
                    # to do the same as the Configuration.setup method
                    if isinstance(value, Value):
                        setup_value(mod, name, value)
                        continue
                setattr(mod, name, value)

            setattr(mod, 'CONFIGURATION', '{0}.{1}'.format(fullname,
                                                           self.name))
            cls.post_setup()

        except Exception as err:
            reraise(err, "Couldn't setup configuration '{0}'".format(cls_path))

        return mod

@@ -1493,189 +1493,219 @@
 y. I
-t uses BaseCommand.use_argparse%0A                    # to decide that, which checks for the option_list list%0A                    base.BaseCommand.option_list += configuration_options
+n that case we should tack our%0A                    # options on to the end of the parser on the way out.%0A                    for option in configuration_options:%0A                        parser.add_option(option)
 %0A   

Fix handling of fallback to optparse.OptionParser