""" the good stuff! the books! """
from uuid import uuid4

from django.contrib.auth.decorators import login_required, permission_required
from django.core.paginator import Paginator
from django.db.models import Avg, Q
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.views import View
from django.views.decorators.http import require_POST

from bookwyrm import forms, models
from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.connectors import connector_manager, ConnectorException
from bookwyrm.connectors.abstract_connector import get_image
from bookwyrm.settings import PAGE_LENGTH
from bookwyrm.views.helpers import is_api_request, maybe_redirect_local_path


# pylint: disable=no-self-use
class Book(View):
    """a book! this is the stuff"""

    def get(self, request, book_id, **kwargs):
        """info about a book"""
        if is_api_request(request):
            book = get_object_or_404(
                models.Book.objects.select_subclasses(), id=book_id
            )
            return ActivitypubResponse(book.to_activity())

        user_statuses = (
            kwargs.get("user_statuses", False)
            if request.user.is_authenticated
            else False
        )

        # it's safe to use this OR because edition and work and subclasses of the same
        # table, so they never have clashing IDs
        book = (
            models.Edition.viewer_aware_objects(request.user)
            .filter(Q(id=book_id) | Q(parent_work__id=book_id))
            .order_by("-edition_rank")
            .select_related("parent_work")
            .prefetch_related("authors", "file_links")
            .first()
        )

        if not book or not book.parent_work:
            raise Http404()

        if redirect_local_path := not user_statuses and maybe_redirect_local_path(
            request, book
        ):
            return redirect_local_path

        # all reviews for all editions of the book
        reviews = models.Review.privacy_filter(request.user).filter(
            book__parent_work__editions=book
        )

        # the reviews to show
        if user_statuses:
            if user_statuses == "review":
                queryset = book.review_set.select_subclasses()
            elif user_statuses == "comment":
                queryset = book.comment_set
            else:
                queryset = book.quotation_set
            queryset = queryset.filter(user=request.user, deleted=False)
        else:
            queryset = reviews.exclude(Q(content__isnull=True) | Q(content=""))
        queryset = queryset.select_related("user").order_by("-published_date")
        paginated = Paginator(queryset, PAGE_LENGTH)

        lists = models.List.privacy_filter(request.user,).filter(
            listitem__approved=True,
            listitem__book__in=book.parent_work.editions.all(),
        )
        data = {
            "book": book,
            "statuses": paginated.get_page(request.GET.get("page")),
            "review_count": reviews.count(),
            "ratings": reviews.filter(
                Q(content__isnull=True) | Q(content="")
            ).select_related("user")
            if not user_statuses
            else None,
            "rating": reviews.aggregate(Avg("rating"))["rating__avg"],
            "lists": lists,
            "update_error": kwargs.get("update_error", False),
        }

        if request.user.is_authenticated:
            data["list_options"] = request.user.list_set.exclude(id__in=data["lists"])
            data["file_link_form"] = forms.FileLinkForm()
            readthroughs = models.ReadThrough.objects.filter(
                user=request.user,
                book=book,
            ).order_by("start_date")

            for readthrough in readthroughs:
                readthrough.progress_updates = (
                    readthrough.progressupdate_set.all().order_by("-updated_date")
                )
            data["readthroughs"] = readthroughs

            data["user_shelfbooks"] = models.ShelfBook.objects.filter(
                user=request.user, book=book
            ).select_related("shelf")

            data["other_edition_shelves"] = models.ShelfBook.objects.filter(
                ~Q(book=book),
                user=request.user,
                book__parent_work=book.parent_work,
            ).select_related("shelf", "book")

            filters = {"user": request.user, "deleted": False}
            data["user_statuses"] = {
                "review_count": book.review_set.filter(**filters).count(),
                "comment_count": book.comment_set.filter(**filters).count(),
                "quotation_count": book.quotation_set.filter(**filters).count(),
            }

        return TemplateResponse(request, "book/book.html", data)


@login_required
@require_POST
def upload_cover(request, book_id):
    """upload a new cover"""
    book = get_object_or_404(models.Edition, id=book_id)
    book.last_edited_by = request.user

    url = request.POST.get("cover-url")
    if url:
        image = set_cover_from_url(url)
        if image:
            book.cover.save(*image)

        return redirect(f"{book.local_path}?cover_error=True")

    form = forms.CoverForm(request.POST, request.FILES, instance=book)
    if not form.is_valid() or not form.files.get("cover"):
        return redirect(book.local_path)

    book.cover = form.files["cover"]
    book.save()

    return redirect(book.local_path)


def set_cover_from_url(url):
    """load it from a url"""
    try:
        image_content, extension = get_image(url)
    except:  # pylint: disable=bare-except
        return None
    if not image_content:
        return None
    image_name = str(uuid4()) + "." + extension
    return [image_name, image_content]


@login_required
@require_POST
@permission_required("bookwyrm.edit_book", raise_exception=True)
def add_description(request, book_id):
    """upload a new cover"""
    book = get_object_or_404(models.Edition, id=book_id)

    description = request.POST.get("description")

    book.description = description
    book.last_edited_by = request.user
    book.save(update_fields=["description", "last_edited_by"])

    return redirect("book", book.id)


@login_required
@require_POST
def resolve_book(request):
    """figure out the local path to a book from a remote_id"""
    remote_id = request.POST.get("remote_id")
    connector = connector_manager.get_or_create_connector(remote_id)
    book = connector.get_or_create_book(remote_id)

    return redirect("book", book.id)


@login_required
@require_POST
@permission_required("bookwyrm.edit_book", raise_exception=True)
# pylint: disable=unused-argument
def update_book_from_remote(request, book_id, connector_identifier):
    """load the remote data for this book"""
    connector = connector_manager.load_connector(
        get_object_or_404(models.Connector, identifier=connector_identifier)
    )
    book = get_object_or_404(models.Book.objects.select_subclasses(), id=book_id)

    try:
        connector.update_book_from_remote(book)
    except ConnectorException:
        # the remote source isn't available or doesn't know this book
        return Book().get(request, book_id, update_error=True)

    return redirect("book", book.id)
