"""
LARA Django project initialization.

_____________________________________________________________________.

:PROJECT: lara-django

*lara-django project intitialisation*

:details: initialise lara-django project (run this only once !)
          this file is highly inspired by
          https://gitlab.com/mayan-edms/mayan-edms/-/blob/master/mayan/apps/common/management/commands/initialsetup.py


:authors: mark doerr (mark@uni-greifswald.de)

:date: (creation)          20200409

.. note:: -
.. todo:: -
________________________________________________________________________
"""


import logging

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.db import DEFAULT_DB_ALIAS, connections

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    """
    Django management command to initialize the LARA project.

    See https://docs.djangoproject.com/en/1.9/howto/custom-management-commands/ for more details.
    Uses the argparse mechanism introduced in Django > 1.8.
    """

    # logging.basicConfig(format='%(levelname)s| %(module)s.%(funcName)s:%(message)s',
    #                     level=logging.DEBUG)  # level=logging.ERROR

    help = 'Initialise the lara-django project with default settings - run only once.'

    def add_arguments(self, parser):
        """Add command-line arguments for this command."""
        # defining named commandline arguments
        parser.add_argument('-c', '--collect-static',
                            action='store_true',
                            help='collect static files into static dir')
        parser.add_argument('-n', '--no-fixtures',
                            action='store_true',
                            help='do not load fixtures')
        parser.add_argument('-d', '--load-demo-data',
                            action='store_true',
                            help='load demo data')
        parser.add_argument(
            '--force', action='store_true',
            help='Force execution of the initialization process.',
        )
        parser.add_argument('--make-all-migrations',
                            action='store_true',
                            help='make all migrations, but do not reset and initialise database')
        parser.add_argument('-f', '--load-fixtures',
                            action='store_true',
                            help='load fixtures')
        # list all apps with versions
        parser.add_argument( '-l',
            '--list-apps', action='store_true',
            help='List all installed apps with their versions.',
        )

        # should be removed
        # parser.add_argument(
        #    '--no-dependencies', action='store_true', dest='no_dependencies',
        #    help='Don\'t download dependencies.',
        # )

    def handle(self, *args, **options):
        """Dispatch based on command-line arguments/options."""
        # check if database is initialised (i.e. auth_user table exists)
        connection = connections[DEFAULT_DB_ALIAS]
        table_name = "auth_group"
        db_initialised = table_name in connection.introspection.table_names()

        logger.info(f"LARA db initialised: {db_initialised}")

        if options['make_all_migrations']:
            self.make_app_migrations(options)
        elif options['load_fixtures']:
            self.load_fixtures(options)
        elif options['list_apps']:
            self.list_apps()
        else:
            logger.info("initialising lara-django project database....")

            if db_initialised and not options.get('force', False):
                logger.info("database already initialised, waiting for db....")
                call_command('wait_for_db')
                return
            else:
                self.initialise_lara_django(force=options.get('force', False))
            # pre_initial_setup.send(sender=self)

            if not options.get('no_dependencies', False):
                # call_command(command_name='install_dependencies')
                # call_command(
                #    command_name='prepare_static', interactive=False
                # )
                pass

        if options['collect_static']:
            call_command('collectstatic', '--noinput')  # interactive=False


        if not options['no_fixtures'] and not db_initialised:
            self.load_fixtures(options)

            # now create the admin / superuser
            call_command('init_admin')

        if options['load_demo_data']:
            self.load_demo_data()

    def initialise_lara_django(self, force=False):
        """
        Initialise lara-django.

        Parameters
        ----------
        force : bool, optional
            Force overwriting of all files.

        """
        # s. mayan
        #system_path = os.path.join(settings.MEDIA_ROOT, SYSTEM_DIR)
        #settings_path = os.path.join(settings.MEDIA_ROOT, 'hellodjango_settings')
        #secret_key_file_path = os.path.join(system_path, SECRET_KEY_FILENAME)

        call_command('wait_for_db')

        call_command('makemigrations')

        if self.num_migrations() > 0:
            logger.debug(f"unresolved {self.num_migrations()} migrations found")
            self.make_app_migrations()

        call_command('migrate')

    def num_migrations(self):
        """Return the number of pending migrations."""
        from django.db.migrations.executor import MigrationExecutor
        try:
            executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
        except ImproperlyConfigured:
            # No databases are configured (or the dummy one)
            return

        plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
        if plan:
            return len(plan)
        else:
            return 0

    def make_app_migrations(self, options=None):
        """
        Make individual app migrations.

        See https://docs.djangoproject.com/en/2.0/ref/django-admin/.
        """
        #db_filename = settings.DATABASES['default']['NAME']
        for app in settings.LARA_APPS:
            logger.debug(f"migrating : {app}")
            call_command('makemigrations', app)
        call_command('migrate')

    def load_fixtures(self, options=None):
        """
        Load fixtures into the LARA database.

        Parameters
        ----------
        options : dict | None
            Command options.

        """
        logger.info("initialising database with fixtures....")
        for fixture in settings.FIXTURES:
            call_command('loaddata', fixture)

    def load_demo_data(self, options=None):
        """
        Load demo data into the LARA database.

        Parameters
        ----------
        options : dict | None
            Command options.

        """
        logger.info("loading demo data....")
        if settings.DEMO_DATA is not None:
            for fixture in settings.DEMO_DATA:
                call_command('loaddata', fixture)

    def list_apps(self):
        """List all installed apps with their versions."""
        logger.info("listing all installed apps with their versions....")
        for app in settings.INSTALLED_APPS:
            try:
                module = __import__(app, fromlist=['__version__'])
                version = getattr(module, '__version__', 'No version found')
            except ImportError:
                version = 'No version found'
            logger.info(f"{app}: {version}")
