""" using django model forms """
import datetime
from collections import defaultdict
from urllib.parse import urlparse

from django import forms
from django.forms import ModelForm, PasswordInput, widgets, ChoiceField
from django.forms.widgets import Textarea
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from bookwyrm import models
from bookwyrm.models.fields import ClearableFileInputWithWarning
from bookwyrm.models.user import FeedFilterChoices


class CustomForm(ModelForm):
    """add css classes to the forms"""

    def __init__(self, *args, **kwargs):
        css_classes = defaultdict(lambda: "")
        css_classes["text"] = "input"
        css_classes["password"] = "input"
        css_classes["email"] = "input"
        css_classes["number"] = "input"
        css_classes["checkbox"] = "checkbox"
        css_classes["textarea"] = "textarea"
        # pylint: disable=super-with-arguments
        super(CustomForm, self).__init__(*args, **kwargs)
        for visible in self.visible_fields():
            if hasattr(visible.field.widget, "input_type"):
                input_type = visible.field.widget.input_type
            if isinstance(visible.field.widget, Textarea):
                input_type = "textarea"
                visible.field.widget.attrs["rows"] = 5
            visible.field.widget.attrs["class"] = css_classes[input_type]


# pylint: disable=missing-class-docstring
class LoginForm(CustomForm):
    class Meta:
        model = models.User
        fields = ["localname", "password"]
        help_texts = {f: None for f in fields}
        widgets = {
            "password": PasswordInput(),
        }


class RegisterForm(CustomForm):
    class Meta:
        model = models.User
        fields = ["localname", "email", "password"]
        help_texts = {f: None for f in fields}
        widgets = {"password": PasswordInput()}

    def clean(self):
        """Check if the username is taken"""
        cleaned_data = super().clean()
        localname = cleaned_data.get("localname").strip()
        if models.User.objects.filter(localname=localname).first():
            self.add_error("localname", _("User with this username already exists"))


class RatingForm(CustomForm):
    class Meta:
        model = models.ReviewRating
        fields = ["user", "book", "rating", "privacy"]


class ReviewForm(CustomForm):
    class Meta:
        model = models.Review
        fields = [
            "user",
            "book",
            "name",
            "content",
            "rating",
            "content_warning",
            "sensitive",
            "privacy",
        ]


class CommentForm(CustomForm):
    class Meta:
        model = models.Comment
        fields = [
            "user",
            "book",
            "content",
            "content_warning",
            "sensitive",
            "privacy",
            "progress",
            "progress_mode",
            "reading_status",
        ]


class QuotationForm(CustomForm):
    class Meta:
        model = models.Quotation
        fields = [
            "user",
            "book",
            "quote",
            "content",
            "content_warning",
            "sensitive",
            "privacy",
            "position",
            "position_mode",
        ]


class ReplyForm(CustomForm):
    class Meta:
        model = models.Status
        fields = [
            "user",
            "content",
            "content_warning",
            "sensitive",
            "reply_parent",
            "privacy",
        ]


class StatusForm(CustomForm):
    class Meta:
        model = models.Status
        fields = ["user", "content", "content_warning", "sensitive", "privacy"]


class DirectForm(CustomForm):
    class Meta:
        model = models.Status
        fields = ["user", "content", "content_warning", "sensitive", "privacy"]


class EditUserForm(CustomForm):
    class Meta:
        model = models.User
        fields = [
            "avatar",
            "name",
            "email",
            "summary",
            "show_goal",
            "show_suggested_users",
            "manually_approves_followers",
            "default_post_privacy",
            "discoverable",
            "preferred_timezone",
            "preferred_language",
        ]
        help_texts = {f: None for f in fields}
        widgets = {
            "avatar": ClearableFileInputWithWarning(
                attrs={"aria-describedby": "desc_avatar"}
            ),
            "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}),
            "summary": forms.Textarea(attrs={"aria-describedby": "desc_summary"}),
            "email": forms.EmailInput(attrs={"aria-describedby": "desc_email"}),
            "discoverable": forms.CheckboxInput(
                attrs={"aria-describedby": "desc_discoverable"}
            ),
        }


class LimitedEditUserForm(CustomForm):
    class Meta:
        model = models.User
        fields = [
            "avatar",
            "name",
            "summary",
            "manually_approves_followers",
            "discoverable",
        ]
        help_texts = {f: None for f in fields}
        widgets = {
            "avatar": ClearableFileInputWithWarning(
                attrs={"aria-describedby": "desc_avatar"}
            ),
            "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}),
            "summary": forms.Textarea(attrs={"aria-describedby": "desc_summary"}),
            "discoverable": forms.CheckboxInput(
                attrs={"aria-describedby": "desc_discoverable"}
            ),
        }


class DeleteUserForm(CustomForm):
    class Meta:
        model = models.User
        fields = ["password"]


class UserGroupForm(CustomForm):
    class Meta:
        model = models.User
        fields = ["groups"]


class FeedStatusTypesForm(CustomForm):
    class Meta:
        model = models.User
        fields = ["feed_status_types"]
        help_texts = {f: None for f in fields}
        widgets = {
            "feed_status_types": widgets.CheckboxSelectMultiple(
                choices=FeedFilterChoices,
            ),
        }


class CoverForm(CustomForm):
    class Meta:
        model = models.Book
        fields = ["cover"]
        help_texts = {f: None for f in fields}


class LinkDomainForm(CustomForm):
    class Meta:
        model = models.LinkDomain
        fields = ["name"]


class FileLinkForm(CustomForm):
    class Meta:
        model = models.FileLink
        fields = ["url", "filetype", "availability", "book", "added_by"]

    def clean(self):
        """make sure the domain isn't blocked or pending"""
        cleaned_data = super().clean()
        url = cleaned_data.get("url")
        filetype = cleaned_data.get("filetype")
        book = cleaned_data.get("book")
        domain = urlparse(url).netloc
        if models.LinkDomain.objects.filter(domain=domain).exists():
            status = models.LinkDomain.objects.get(domain=domain).status
            if status == "blocked":
                # pylint: disable=line-too-long
                self.add_error(
                    "url",
                    _(
                        "This domain is blocked. Please contact your administrator if you think this is an error."
                    ),
                )
            elif models.FileLink.objects.filter(
                url=url, book=book, filetype=filetype
            ).exists():
                # pylint: disable=line-too-long
                self.add_error(
                    "url",
                    _(
                        "This link with file type has already been added for this book. If it is not visible, the domain is still pending."
                    ),
                )


class EditionForm(CustomForm):
    class Meta:
        model = models.Edition
        exclude = [
            "remote_id",
            "origin_id",
            "created_date",
            "updated_date",
            "edition_rank",
            "authors",
            "parent_work",
            "shelves",
            "connector",
            "search_vector",
            "links",
            "file_links",
        ]
        widgets = {
            "title": forms.TextInput(attrs={"aria-describedby": "desc_title"}),
            "subtitle": forms.TextInput(attrs={"aria-describedby": "desc_subtitle"}),
            "description": forms.Textarea(
                attrs={"aria-describedby": "desc_description"}
            ),
            "series": forms.TextInput(attrs={"aria-describedby": "desc_series"}),
            "series_number": forms.TextInput(
                attrs={"aria-describedby": "desc_series_number"}
            ),
            "languages": forms.TextInput(
                attrs={"aria-describedby": "desc_languages_help desc_languages"}
            ),
            "publishers": forms.TextInput(
                attrs={"aria-describedby": "desc_publishers_help desc_publishers"}
            ),
            "first_published_date": forms.SelectDateWidget(
                attrs={"aria-describedby": "desc_first_published_date"}
            ),
            "published_date": forms.SelectDateWidget(
                attrs={"aria-describedby": "desc_published_date"}
            ),
            "cover": ClearableFileInputWithWarning(
                attrs={"aria-describedby": "desc_cover"}
            ),
            "physical_format": forms.Select(
                attrs={"aria-describedby": "desc_physical_format"}
            ),
            "physical_format_detail": forms.TextInput(
                attrs={"aria-describedby": "desc_physical_format_detail"}
            ),
            "pages": forms.NumberInput(attrs={"aria-describedby": "desc_pages"}),
            "isbn_13": forms.TextInput(attrs={"aria-describedby": "desc_isbn_13"}),
            "isbn_10": forms.TextInput(attrs={"aria-describedby": "desc_isbn_10"}),
            "openlibrary_key": forms.TextInput(
                attrs={"aria-describedby": "desc_openlibrary_key"}
            ),
            "inventaire_id": forms.TextInput(
                attrs={"aria-describedby": "desc_inventaire_id"}
            ),
            "oclc_number": forms.TextInput(
                attrs={"aria-describedby": "desc_oclc_number"}
            ),
            "ASIN": forms.TextInput(attrs={"aria-describedby": "desc_ASIN"}),
        }


class AuthorForm(CustomForm):
    class Meta:
        model = models.Author
        fields = [
            "last_edited_by",
            "name",
            "aliases",
            "bio",
            "wikipedia_link",
            "born",
            "died",
            "openlibrary_key",
            "inventaire_id",
            "librarything_key",
            "goodreads_key",
            "isni",
        ]
        widgets = {
            "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}),
            "aliases": forms.TextInput(attrs={"aria-describedby": "desc_aliases"}),
            "bio": forms.Textarea(attrs={"aria-describedby": "desc_bio"}),
            "wikipedia_link": forms.TextInput(
                attrs={"aria-describedby": "desc_wikipedia_link"}
            ),
            "born": forms.SelectDateWidget(attrs={"aria-describedby": "desc_born"}),
            "died": forms.SelectDateWidget(attrs={"aria-describedby": "desc_died"}),
            "oepnlibrary_key": forms.TextInput(
                attrs={"aria-describedby": "desc_oepnlibrary_key"}
            ),
            "inventaire_id": forms.TextInput(
                attrs={"aria-describedby": "desc_inventaire_id"}
            ),
            "librarything_key": forms.TextInput(
                attrs={"aria-describedby": "desc_librarything_key"}
            ),
            "goodreads_key": forms.TextInput(
                attrs={"aria-describedby": "desc_goodreads_key"}
            ),
        }


class ImportForm(forms.Form):
    csv_file = forms.FileField()


class ExpiryWidget(widgets.Select):
    def value_from_datadict(self, data, files, name):
        """human-readable exiration time buckets"""
        selected_string = super().value_from_datadict(data, files, name)

        if selected_string == "day":
            interval = datetime.timedelta(days=1)
        elif selected_string == "week":
            interval = datetime.timedelta(days=7)
        elif selected_string == "month":
            interval = datetime.timedelta(days=31)  # Close enough?
        elif selected_string == "forever":
            return None
        else:
            return selected_string  # This will raise

        return timezone.now() + interval


class InviteRequestForm(CustomForm):
    def clean(self):
        """make sure the email isn't in use by a registered user"""
        cleaned_data = super().clean()
        email = cleaned_data.get("email")
        if email and models.User.objects.filter(email=email).exists():
            self.add_error("email", _("A user with this email already exists."))

    class Meta:
        model = models.InviteRequest
        fields = ["email"]


class CreateInviteForm(CustomForm):
    class Meta:
        model = models.SiteInvite
        exclude = ["code", "user", "times_used", "invitees"]
        widgets = {
            "expiry": ExpiryWidget(
                choices=[
                    ("day", _("One Day")),
                    ("week", _("One Week")),
                    ("month", _("One Month")),
                    ("forever", _("Does Not Expire")),
                ]
            ),
            "use_limit": widgets.Select(
                choices=[(i, _(f"{i} uses")) for i in [1, 5, 10, 25, 50, 100]]
                + [(None, _("Unlimited"))]
            ),
        }


class ShelfForm(CustomForm):
    class Meta:
        model = models.Shelf
        fields = ["user", "name", "privacy", "description"]


class GoalForm(CustomForm):
    class Meta:
        model = models.AnnualGoal
        fields = ["user", "year", "goal", "privacy"]


class SiteForm(CustomForm):
    class Meta:
        model = models.SiteSettings
        exclude = ["admin_code", "install_mode"]
        widgets = {
            "instance_short_description": forms.TextInput(
                attrs={"aria-describedby": "desc_instance_short_description"}
            ),
            "require_confirm_email": forms.CheckboxInput(
                attrs={"aria-describedby": "desc_require_confirm_email"}
            ),
            "invite_request_text": forms.Textarea(
                attrs={"aria-describedby": "desc_invite_request_text"}
            ),
        }


class AnnouncementForm(CustomForm):
    class Meta:
        model = models.Announcement
        exclude = ["remote_id"]
        widgets = {
            "preview": forms.TextInput(attrs={"aria-describedby": "desc_preview"}),
            "content": forms.Textarea(attrs={"aria-describedby": "desc_content"}),
            "event_date": forms.SelectDateWidget(
                attrs={"aria-describedby": "desc_event_date"}
            ),
            "start_date": forms.SelectDateWidget(
                attrs={"aria-describedby": "desc_start_date"}
            ),
            "end_date": forms.SelectDateWidget(
                attrs={"aria-describedby": "desc_end_date"}
            ),
            "active": forms.CheckboxInput(attrs={"aria-describedby": "desc_active"}),
        }


class ListForm(CustomForm):
    class Meta:
        model = models.List
        fields = ["user", "name", "description", "curation", "privacy", "group"]


class ListItemForm(CustomForm):
    class Meta:
        model = models.ListItem
        fields = ["user", "book", "book_list", "notes"]


class GroupForm(CustomForm):
    class Meta:
        model = models.Group
        fields = ["user", "privacy", "name", "description"]


class ReportForm(CustomForm):
    class Meta:
        model = models.Report
        fields = ["user", "reporter", "statuses", "links", "note"]


class EmailBlocklistForm(CustomForm):
    class Meta:
        model = models.EmailBlocklist
        fields = ["domain"]
        widgets = {
            "avatar": forms.TextInput(attrs={"aria-describedby": "desc_domain"}),
        }


class IPBlocklistForm(CustomForm):
    class Meta:
        model = models.IPBlocklist
        fields = ["address"]


class ServerForm(CustomForm):
    class Meta:
        model = models.FederatedServer
        exclude = ["remote_id"]


class SortListForm(forms.Form):
    sort_by = ChoiceField(
        choices=(
            ("order", _("List Order")),
            ("title", _("Book Title")),
            ("rating", _("Rating")),
        ),
        label=_("Sort By"),
    )
    direction = ChoiceField(
        choices=(
            ("ascending", _("Ascending")),
            ("descending", _("Descending")),
        ),
    )


class ReadThroughForm(CustomForm):
    def clean(self):
        """make sure the email isn't in use by a registered user"""
        cleaned_data = super().clean()
        start_date = cleaned_data.get("start_date")
        finish_date = cleaned_data.get("finish_date")
        if start_date and finish_date and start_date > finish_date:
            self.add_error(
                "finish_date", _("Reading finish date cannot be before start date.")
            )

    class Meta:
        model = models.ReadThrough
        fields = ["user", "book", "start_date", "finish_date"]
