from functools import partial
from typing import ClassVar, Dict, List

from django import forms
from django.core.files import File
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views.generic import FormView

from core.models.config import Config, UploadedImage
from users.decorators import identity_required


@method_decorator(identity_required, name="dispatch")
class SettingsPage(FormView):
    """
    Shows a settings page dynamically created from our settings layout
    at the bottom of the page. Don't add this to a URL directly - subclass!
    """

    options_class = Config.IdentityOptions
    template_name = "settings/settings.html"
    section: ClassVar[str]
    options: Dict[str, Dict[str, str]]
    layout: Dict[str, List[str]]

    def get_form_class(self):
        # Create the fields dict from the config object
        fields = {}
        for key, details in self.options.items():
            field_kwargs = {}
            config_field = self.options_class.__fields__[key]
            if config_field.type_ is bool:
                form_field = partial(
                    forms.BooleanField,
                    widget=forms.Select(
                        choices=[(True, "Enabled"), (False, "Disabled")]
                    ),
                )
            elif config_field.type_ is UploadedImage:
                form_field = forms.ImageField
            elif config_field.type_ is str:
                if details.get("display") == "textarea":
                    form_field = partial(
                        forms.CharField,
                        widget=forms.Textarea,
                    )
                else:
                    form_field = forms.CharField
            elif config_field.type_ is int:
                choices = details.get("choices")
                if choices:
                    field_kwargs["widget"] = forms.Select(choices=choices)
                form_field = forms.IntegerField
            else:
                raise ValueError(f"Cannot render settings type {config_field.type_}")
            fields[key] = form_field(
                label=details["title"],
                help_text=details.get("help_text", ""),
                required=details.get("required", False),
                **field_kwargs,
            )
        # Create a form class dynamically (yeah, right?) and return that
        return type("SettingsForm", (forms.Form,), fields)

    def load_config(self):
        return Config.load_identity(self.request.identity)

    def save_config(self, key, value):
        Config.set_identity(self.request.identity, key, value)

    def get_initial(self):
        config = self.load_config()
        initial = {}
        for key in self.options.keys():
            initial[key] = getattr(config, key)
        return initial

    def get_context_data(self):
        context = super().get_context_data()
        context["section"] = self.section
        # Gather fields into fieldsets
        context["fieldsets"] = {}
        for title, fields in self.layout.items():
            context["fieldsets"][title] = [context["form"][field] for field in fields]
        return context

    def form_valid(self, form):
        # Save each key
        for field in form:
            if field.field.__class__.__name__ == "ImageField":
                # These can be cleared with an extra checkbox
                if self.request.POST.get(f"{field.name}__clear"):
                    self.save_config(field.name, None)
                    continue
                # We shove the preview values in initial_data, so only save file
                # fields if they have a File object.
                if not isinstance(form.cleaned_data[field.name], File):
                    continue
            self.save_config(
                field.name,
                form.cleaned_data[field.name],
            )
        return redirect(".")


from activities.models.post import Post


class InterfacePage(SettingsPage):

    section = "interface"

    options = {
        "toot_mode": {
            "title": "I Will Toot As I Please",
            "help_text": "Changes all 'Post' buttons to 'Toot!'",
        },
        "default_post_visibility": {
            "title": "Default Post Visibility",
            "help_text": "Visibility to use as default for new posts.",
            "choices": Post.Visibilities.choices,
        },
    }

    layout = {"Posting": ["toot_mode", "default_post_visibility"]}
