import os

from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.core.files.storage import FileSystemStorage
from django.forms import Form
from django.template.response import SimpleTemplateResponse
from django.urls import NoReverseMatch
from formtools.wizard.views import SessionWizardView

from cms.models import Page
from cms.utils import get_current_site
from cms.utils.i18n import get_site_language_from_request

from .forms import WizardStep1Form, WizardStep2BaseForm, step2_form_factory
from .wizard_pool import wizard_pool


class WizardCreateView(SessionWizardView):
    template_name = 'cms/wizards/start.html'
    file_storage = FileSystemStorage(
        location=os.path.join(settings.MEDIA_ROOT, 'wizard_tmp_files'))

    form_list = [
        ('0', WizardStep1Form),
        # Form is used as a placeholder form.
        # the real form will be loaded after step 0
        ('1', Form),
    ]

    def dispatch(self, *args, **kwargs):
        user = self.request.user

        if not user.is_active or not user.is_staff:
            raise PermissionDenied
        self.site = get_current_site()
        return super().dispatch(*args, **kwargs)

    def get_current_step(self):
        """Returns the current step, if possible, else None."""
        try:
            return self.steps.current
        except AttributeError:
            return None

    def is_first_step(self, step=None):
        step = step or self.get_current_step()
        return step == '0'

    def is_second_step(self, step=None):
        step = step or self.get_current_step()
        return step == '1'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        if self.is_second_step():
            context['wizard_entry'] = self.get_selected_entry()
        return context

    def get_form(self, step=None, data=None, files=None):
        if step is None:
            step = self.steps.current

        # We need to grab the page from pre-validated data so that the wizard
        # has it to prepare the list of valid entries.
        if data:
            page_key = f"{step}-page"
            self.page_pk = data.get(page_key, None)
        else:
            self.page_pk = None

        if self.is_second_step(step):
            self.form_list[step] = self.get_step_2_form(step, data, files)
        return super().get_form(step, data, files)

    def get_form_kwargs(self, step=None):
        """This is called by self.get_form()"""
        kwargs = super().get_form_kwargs()
        kwargs['wizard_request'] = self.request
        kwargs['wizard_site'] = self.site
        if self.is_second_step(step):
            kwargs['wizard_page'] = self.get_origin_page()
            kwargs['wizard_language'] = self.get_origin_language()
        else:
            page_pk = self.page_pk or self.request.GET.get('page', None)
            if page_pk and page_pk != 'None':
                kwargs['wizard_page'] = Page.objects.filter(pk=page_pk).first()
            else:
                kwargs['wizard_page'] = None
            kwargs['wizard_language'] = get_site_language_from_request(
                self.request,
                site_id=self.site.pk,
            )
        return kwargs

    def get_form_initial(self, step):
        """This is called by self.get_form()"""
        initial = super().get_form_initial(step)
        if self.is_first_step(step):
            initial['page'] = self.request.GET.get('page')
            initial['language'] = self.request.GET.get('language')
        return initial

    def get_step_2_form(self, step=None, data=None, files=None):
        entry_form_class = self.get_selected_entry().form
        step_2_base_form = self.get_step_2_base_form()

        form = step2_form_factory(
            mixin_cls=step_2_base_form,
            entry_form_class=entry_form_class,
        )
        return form

    def get_step_2_base_form(self):
        """
        Returns the base form to be used for step 2.
        This form is sub classed dynamically by the form defined per module.
        """
        return WizardStep2BaseForm

    def get_template_names(self):
        if self.is_first_step():
            template_name = self.template_name
        else:
            template_name = self.get_selected_entry().template_name
        return template_name

    def done(self, form_list, **kwargs):
        """
        This step only runs if all forms are valid. Simply emits a simple
        template that uses JS to redirect to the newly created object.
        """
        form_one, form_two = list(form_list)
        instance = form_two.save()
        url = self.get_success_url(instance)
        language = form_one.cleaned_data['language']
        if not url:
            page = self.get_origin_page()
            if page:
                try:
                    url = page.get_absolute_url(language)
                except NoReverseMatch:
                    url = '/'
            else:
                url = '/'

        return SimpleTemplateResponse("cms/wizards/done.html", {"url": url})

    def get_selected_entry(self):
        data = self.get_cleaned_data_for_step('0')
        return wizard_pool.get_entry(data['entry'])

    def get_origin_page(self):
        data = self.get_cleaned_data_for_step('0')
        return data.get('page')

    def get_origin_language(self):
        data = self.get_cleaned_data_for_step('0')
        return data.get('language')

    def get_success_url(self, instance):
        entry = self.get_selected_entry()
        language = self.get_origin_language()
        success_url = entry.get_success_url(
            obj=instance,
            language=language,
        )
        return success_url
