from django import forms
from django.urls import reverse

from common.utils import safe_getattr
from games.models import Device, Game, Platform, Purchase, Session

custom_date_widget = forms.DateInput(attrs={"type": "date"})
custom_datetime_widget = forms.DateTimeInput(
    attrs={"type": "datetime-local"}, format="%Y-%m-%d %H:%M"
)
autofocus_input_widget = forms.TextInput(attrs={"autofocus": "autofocus"})


class MultipleGameChoiceField(forms.ModelMultipleChoiceField):
    def label_from_instance(self, obj) -> str:
        return f"{obj.sort_name} ({obj.platform}, {obj.year_released})"


class SingleGameChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj) -> str:
        return f"{obj.sort_name} ({obj.platform}, {obj.year_released})"


class SessionForm(forms.ModelForm):
    game = SingleGameChoiceField(
        queryset=Game.objects.order_by("sort_name"),
        widget=forms.Select(attrs={"autofocus": "autofocus"}),
    )

    device = forms.ModelChoiceField(queryset=Device.objects.order_by("name"))

    mark_as_played = forms.BooleanField(
        required=False,
        initial={"mark_as_played": True},
        label="Set game status to Played if Unplayed",
    )

    class Meta:
        widgets = {
            "timestamp_start": custom_datetime_widget,
            "timestamp_end": custom_datetime_widget,
        }
        model = Session
        fields = [
            "game",
            "timestamp_start",
            "timestamp_end",
            "duration_manual",
            "emulated",
            "device",
            "note",
            "mark_as_played",
        ]

    def save(self, commit=True):
        session = super().save(commit=False)
        if self.cleaned_data.get("mark_as_played"):
            game_instance = session.game
            if game_instance.status == "u":
                game_instance.status = "p"
            if commit:
                game_instance.save()
        if commit:
            session.save()
        return session


class IncludePlatformSelect(forms.SelectMultiple):
    def create_option(self, name, value, *args, **kwargs):
        option = super().create_option(name, value, *args, **kwargs)
        if platform_id := safe_getattr(value, "instance.platform.id"):
            option["attrs"]["data-platform"] = platform_id
        return option


class PurchaseForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Automatically update related_purchase <select/>
        # to only include purchases of the selected game.
        related_purchase_by_game_url = reverse("related_purchase_by_game")
        self.fields["games"].widget.attrs.update(
            {
                "hx-trigger": "load, click",
                "hx-get": related_purchase_by_game_url,
                "hx-target": "#id_related_purchase",
                "hx-swap": "outerHTML",
            }
        )

    games = MultipleGameChoiceField(
        queryset=Game.objects.order_by("sort_name"),
        widget=IncludePlatformSelect(attrs={"autoselect": "autoselect"}),
    )
    platform = forms.ModelChoiceField(queryset=Platform.objects.order_by("name"))
    related_purchase = forms.ModelChoiceField(
        queryset=Purchase.objects.filter(type=Purchase.GAME),
        required=False,
    )

    class Meta:
        widgets = {
            "date_purchased": custom_date_widget,
            "date_refunded": custom_date_widget,
            "date_finished": custom_date_widget,
            "date_dropped": custom_date_widget,
        }
        model = Purchase
        fields = [
            "games",
            "platform",
            "date_purchased",
            "date_refunded",
            "date_finished",
            "date_dropped",
            "infinite",
            "price",
            "price_currency",
            "ownership_type",
            "type",
            "related_purchase",
            "name",
        ]

    def clean(self):
        cleaned_data = super().clean()
        purchase_type = cleaned_data.get("type")
        related_purchase = cleaned_data.get("related_purchase")
        name = cleaned_data.get("name")

        # Set the type on the instance to use get_type_display()
        # This is safe because we're not saving the instance.
        self.instance.type = purchase_type

        if purchase_type != Purchase.GAME:
            type_display = self.instance.get_type_display()
            if not related_purchase:
                self.add_error(
                    "related_purchase",
                    f"{type_display} must have a related purchase.",
                )
            if not name:
                self.add_error("name", f"{type_display} must have a name.")
        return cleaned_data


class IncludeNameSelect(forms.Select):
    def create_option(self, name, value, *args, **kwargs):
        option = super().create_option(name, value, *args, **kwargs)
        if value:
            option["attrs"]["data-name"] = value.instance.name
            option["attrs"]["data-year"] = value.instance.year_released
        return option


class GameModelChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        # Use sort_name as the label for the option
        return obj.sort_name


class GameForm(forms.ModelForm):
    platform = forms.ModelChoiceField(
        queryset=Platform.objects.order_by("name"), required=False
    )

    class Meta:
        model = Game
        fields = [
            "name",
            "sort_name",
            "platform",
            "year_released",
            "status",
            "mastered",
            "wikidata",
        ]
        widgets = {"name": autofocus_input_widget}


class PlatformForm(forms.ModelForm):
    class Meta:
        model = Platform
        fields = [
            "name",
            "icon",
            "group",
        ]
        widgets = {"name": autofocus_input_widget}


class DeviceForm(forms.ModelForm):
    class Meta:
        model = Device
        fields = ["name", "type"]
        widgets = {"name": autofocus_input_widget}