from django.core.management.base import BaseCommand, CommandError, no_translations
from django.core.management.commands.makemigrations import Command as makeMigrationsCommand
from django.core.management import call_command
from django.utils.timezone import now
from after_deploy.models import tasks as taskModel
from after_deploy import get_version
import os, sys, pathlib, importlib, traceback

ROOT = os.path.abspath(os.getcwd())

HEADER_TEMPLATE = """\
# Generated by After Deploy %(version)s on %(timestamp)s

"""

TEMPLATE = """\
%(migration_header)s%(imports)s

# Put your task implementation HERE.

# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_deploy --run).
if not tasks.objects.filter(pk='%(task_code)s'):
    cursor = connection.cursor()
    cursor.execute("INSERT INTO after_deploy_tasks (code) VALUES ('%(task_code)s')")

"""

class MyMigrationMaker(makeMigrationsCommand):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @no_translations
    def handle(self, *tasks, **kwargs):
        self.verbosity = 1
        self.write_migration_files(tasks)

    def write_migration_files(self, in_tasks):
        items = {
            'migration_header': HEADER_TEMPLATE % { 'version': get_version(), 'timestamp': now().strftime('%Y-%m-%d %H:%M'), }
        }
        items['imports'] = 'from after_deploy.models import tasks\nfrom django.db import connection'

        for task_name in in_tasks:
            task_code = now().strftime('%Y%m%d%H%M%S')
            items['task_code'] = task_code
            task_string = TEMPLATE % items
            with open(f'{ROOT}/tasks/_{task_code}_{task_name}.py', 'w', encoding='utf-8') as fh:
                fh.write(task_string)
            if self.verbosity >= 1:
                self.stdout.write(self.style.SUCCESS(f'After Deploy generated task: {task_name}.'))

class Command(BaseCommand):
    help = 'Install after-deploy on Django application, Generate new tasks and run tasks.'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def add_arguments(self, parser):
        parser.add_argument('--install', action='store_true', help='Install after deploy',)
        parser.add_argument('-g', '--generate', action='store_true', help='Create a new task',)
        parser.add_argument('-r', '--run', action='store_true', help='Run all unapplied tasks or select tasks',)
        parser.add_argument('tasks', nargs='*', type=str, help='Tasks names to generate or run.',)

    def get_version(self):
        return f'AfterDeploy version {str(get_version())}'

    def handle(self, *args, **kwargs):
        install   = kwargs['install']
        generate  = kwargs['generate']
        run       = kwargs['run']
        tasks     = kwargs['tasks']
        traceback = kwargs['traceback']

        if install:
            try:
                os.chdir(ROOT)
                self.stdout.write('Installing django-after-deploy on your app...')
                call_command('makemigrations', 'after_deploy', verbosity=0, interactive=False)
                call_command('migrate',        'after_deploy', verbosity=0, interactive=False)

                pathlib.Path('tasks/').mkdir(parents=True, exist_ok=True)

                init_path = os.path.join(os.path.dirname(f'{ROOT}/tasks/'), '__init__.py')
                if not os.path.isfile(init_path):
                    open(init_path, 'w').close()

                self.stdout.write(self.style.SUCCESS('Successfully installed django-after-deploy.'))
            except Exception as error:
                raise CommandError(f'Error while installing after-deploy {error}')

        elif generate:
            if tasks:
                MyMigrationMaker().handle(*tasks, **kwargs)
            else:
                self.stdout.write(self.style.WARNING('WARNING: After deploy do not provide auto generate file, please set a task name.'))

        elif run:
            (_, _, filenames) = next(os.walk(f'{ROOT}/tasks/'))
            all_dir_tasks     = sorted(list(set([fn.split('_')[1] for fn in sorted(filenames) if fn.split('_')[1]])))
            all_db_tasks      = [v[0] for v in taskModel.objects.all().order_by('pk').values_list('code')]

            if tasks:
                for task in tasks:
                    current_task_name = [s for s in filenames if task in s][0].replace('.py', '')
                    self.execute_task(current_task_name, traceback)
            else:
                for dir_task in all_dir_tasks:
                    if not dir_task in all_db_tasks:
                        current_task_name = [s for s in filenames if dir_task in s][0].replace('.py', '')
                        self.execute_task(current_task_name, traceback)

    def execute_task(self, task_name, enable_traceback=None):
        try:
            importlib.import_module(f'tasks.{task_name}')
            self.stdout.write(self.style.SUCCESS(f'Task {task_name} successfully executed.'))
        except ImportError as error:
            if enable_traceback:
                self.stdout.write(self.style.ERROR(f'Task {task_name} fail.\nError: {error}\nTraceback: {traceback.format_exc()}'))
            else:
                self.stdout.write(self.style.ERROR(f'Task {task_name} fail.\nError: {error}'))
        except Exception as error:
            if enable_traceback:
                self.stdout.write(self.style.ERROR(f'Task {task_name} fail.\nError: {error}\nTraceback: {traceback.format_exc()}'))
            else:
                self.stdout.write(self.style.ERROR(f'Task {task_name} fail.\nError: {error}'))
