import json
from dataclasses import fields
from typing import Union, get_args, get_origin

from django import forms


class ExportScrobbleForm(forms.Form):
    """Provide options for downloading scrobbles"""

    EXPORT_TYPES = (
        ("as", "Audioscrobbler"),
        ("csv", "CSV"),
        ("html", "HTML"),
    )
    export_type = forms.ChoiceField(choices=EXPORT_TYPES)


class ScrobbleForm(forms.Form):
    item_id = forms.CharField(
        label="",
        widget=forms.TextInput(
            attrs={
                "class": "form-control form-control-dark w-100",
                "placeholder": "Scrobble something (ttIMDB, -v Video Game title, -b Book title, -s TheSportsDB ID, -f Food name - calories, or type a question for an AI agent)",
                "aria-label": "Scrobble something",
            }
        ),
    )


# Mapping of types to Django form field classes
TYPE_FIELD_MAP = {
    int: forms.IntegerField,
    float: forms.FloatField,
    bool: forms.BooleanField,
    str: forms.CharField,
    dict: forms.JSONField,
    list: forms.JSONField,
}

# Optional: type-to-widget mapping
TYPE_WIDGET_MAP = {
    str: forms.TextInput(attrs={"size": 80}),
    dict: forms.Textarea(attrs={"rows": 10, "cols": 80}),
    list: forms.Textarea(attrs={"rows": 6, "cols": 80}),
    bool: forms.CheckboxInput(),
}


def django_form_field_from_type(field_type, required=True):
    origin = get_origin(field_type)

    # Handle Optional / Union
    if origin is Union:
        args = get_args(field_type)
        if type(None) in args:
            required = False
            non_none_type = [arg for arg in args if arg is not type(None)][0]
            return django_form_field_from_type(non_none_type, required=required)

    # Determine actual type
    base_type = origin if origin else field_type
    field_class = TYPE_FIELD_MAP.get(base_type, forms.CharField)
    widget = TYPE_WIDGET_MAP.get(base_type)

    return (
        field_class(required=required, widget=widget)
        if widget
        else field_class(required=required)
    )


def form_from_dataclass(dataclass):
    form_fields = {}
    override_fields = {}
    for klass in dataclass.mro():
        if hasattr(klass, "override_fields"):
            override_fields.update(klass.override_fields())
    for f in fields(dataclass):
        if f.name in override_fields:
            form_fields[f.name] = override_fields[f.name]
            continue

        required = f.default is None and f.default_factory is None
        form_fields[f.name] = django_form_field_from_type(f.type, required=required)

        if f.name in dataclass._excluded_fields:
            form_fields[f.name].disabled = True

    form_cls = type(f"{dataclass.__name__}Form", (forms.Form,), form_fields)

    if "notes" in form_cls.base_fields and "notes" not in override_fields:
        form_cls.base_fields["notes"] = forms.CharField(
            required=False,
            widget=forms.Textarea(attrs={"rows": 4}),
        )

        def clean_notes(self):
            notes_str = self.cleaned_data.get("notes", "")
            return [line.strip() for line in notes_str.splitlines() if line.strip()]

        form_cls.clean_notes = clean_notes
    return form_cls


class NotesDictWidget(forms.Widget):
    template_name = "tasks/task_notes_widget.html"

    class Media:
        js = ("tasks/task_notes.js",)

    def value_from_datadict(self, data, files, name):
        timestamps = data.getlist(f"{name}_timestamps")
        if timestamps:
            contents = data.getlist(f"{name}_contents")
            result = {}
            for i, ts in enumerate(timestamps):
                if i < len(contents) and ts:
                    result[ts] = contents[i]
            return result if result else ""
        return data.get(name, "")

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        notes = {}
        if value:
            if isinstance(value, str):
                try:
                    notes = json.loads(value)
                except (json.JSONDecodeError, TypeError):
                    notes = {}
            elif isinstance(value, dict):
                notes = value
        context["widget"]["notes"] = notes
        return context


class NotesDictField(forms.Field):
    widget = NotesDictWidget

    def clean(self, value):
        if not value:
            return {}

        if isinstance(value, str):
            if value.strip():
                from scrobbles.utils import make_note_timestamp

                return {make_note_timestamp(): value.strip()}
            return {}

        if isinstance(value, dict):
            return value

        return {}
