import json
from dataclasses import asdict, dataclass
from typing import Optional

from dataclass_wizard import JSONWizard
from django import forms
from django.contrib.auth import get_user_model
from people.models import Person
from scrobbles.forms import form_from_dataclass

User = get_user_model()


class ScrobbleLogDataEncoder(json.JSONEncoder):
    def default(self, o):
        try:
            return o.__dict__
        except AttributeError:
            return {}


class ScrobbleLogDataDecoder(json.JSONDecoder):
    def default(self, o):
        return o.__dict__


class JSONDataclass(JSONWizard):
    @property
    def asdict(self):
        return asdict(self)

    @property
    def json(self):
        return json.dumps(self.asdict)


@dataclass
class BaseLogData(JSONDataclass):
    description: Optional[str] = None
    notes: Optional[list[str]] = None

    _excluded_fields = {}

    @classmethod
    def form(cls):
        return form_from_dataclass(cls)

    @classmethod
    def override_fields(cls) -> dict:
        return {}


@dataclass
class LongPlayLogData(JSONDataclass):
    long_play_complete: bool = False


@dataclass
class WithPeopleLogData(JSONDataclass):
    with_people_ids: Optional[list[int]] = None

    @property
    def with_people(self) -> list["Person"]:
        from people.models import Person

        if not self.with_people_ids:
            return []
        return [
            Person.objects.filter(id=pid).first()
            for pid in self.with_people_ids
        ]

    @classmethod
    def override_fields(cls) -> dict:
        fields = {}
        for base in cls.mro()[1:]:
            if hasattr(base, "override_fields"):
                base_fields = base.override_fields()
                fields.update(base_fields)
        custom_fields = {
            "with_people_ids": forms.ModelMultipleChoiceField(
                queryset=Person.objects.all(),
                required=False,
                widget=forms.SelectMultiple(attrs={"size": 10}),
            )
        }
        fields.update(custom_fields)
        return fields
