from functools import cached_property
from logging import getLogger
from plone import api
from plone.base.batch import Batch
from plone.memoize.view import memoize
from ploneintranet.core import _
from ploneintranet.layout.interfaces import IAppView
from ploneintranet.userprofile.content.userprofile import IUserProfile
from Products.CMFCore.permissions import AddPortalContent
from Products.CMFPlone.browser.search import munge_search_term
from Products.Five import BrowserView
from Products.ZCTextIndex.ParseTree import ParseError
from zope.interface import implementer

logger = getLogger(__name__)


@implementer(IAppView)
class ImageBankBaseView(BrowserView):
    """The base Image bank app view."""

    batch_size = 12
    batch_form_keys = ["SearchableText"]

    app_name = "image-bank"

    allowed_collection_types = ["Collection", "Folder"]

    @cached_property
    def app(self):
        """Return the collections container."""
        return api.portal.get().apps.get("image-bank")

    @cached_property
    def tabs(self):
        """Return the tabs to show in the app."""
        tabs = [
            {"title": _("label_shared", default="Shared"), "url": self.app.app_url()},
            {
                "title": _("label_private", default="Private"),
                "url": f"{self.app.absolute_url()}/@@app-image-bank-private",
            },
        ]
        current_url = self.request.getURL()
        for tab in tabs:
            tab["current"] = current_url == tab["url"]

        return tabs

    @cached_property
    def collections_container(self):
        """Return the collections container."""
        return api.portal.get().get("image-bank-collections")

    @memoize
    def can_add_collections(self) -> bool:
        """Check if the user has permission to add collections
        in the collections container."""
        return api.user.has_permission(AddPortalContent, obj=self.collections_container)

    @cached_property
    def batch_start(self):
        try:
            return int(self.request.get("b_start", 0))
        except (ValueError, TypeError):
            return 0

    @cached_property
    def base_query(self) -> dict:
        """This needs to be implemented in the subclass.
        It should return a dictionary that can will be used as to query the catalog.
        """
        raise NotImplementedError("Subclasses must implement the query property.")

    @cached_property
    def query(self) -> dict:
        """Take the base query and add the SearchableText
        from the request if it exists.
        """
        query = self.base_query
        searchable_text = munge_search_term(self.request.form.get("SearchableText", ""))
        if searchable_text:
            query["SearchableText"] = searchable_text
        return query

    @cached_property
    def batch(self) -> Batch:
        """Return a batch of brains based on the query.

        If the query is malformed, we try again without the SearchableText.
        """
        query = self.query
        try:
            brains = api.content.find(**query)
        except ParseError:
            query.pop("SearchableText", None)
            brains = api.content.find(**query)
        return Batch(brains, self.batch_size, self.batch_start, orphan=1)

    def has_collections(self) -> bool:
        """Return True if there are any collections to show."""
        return self.batch_start == 0 and len(self.batch) > 0

    @cached_property
    def batch_next_url(self):
        if self.batch.islastpage:
            return None
        form = self.request.form
        params = {key: form[key] for key in self.batch_form_keys if key in form}
        _page, query_string = next(self.batch.nexturls(params))
        return f"{self.request.getURL()}?{query_string}"

    @cached_property
    def back_button_url(self):
        """Return the URL for the back button.

        If the collection is in the context of a user profile,
        return the URL to the private image bank.

        Otherwise return the URL to the app main view.
        """
        if api.content.get_closest_ancestor(self.context, interface=IUserProfile):
            return f"{self.app.absolute_url()}/@@app-image-bank-private"
        return self.app.app_url()

    def brain_to_scale_url(self, brain, scale="large") -> str:
        """Given a brain and a scale name, return the URL to the scaled image
        if it exists, otherwise return the URL to the original image.
        """
        url = brain.getURL()

        try:
            path = brain.image_scales["image"][0]["scales"][scale]["download"]
        except (AttributeError, IndexError, KeyError):
            return url
        except Exception:
            logger.exception(
                "Error computing scale URL for brain %r and scale %r",
                brain.getPath(),
                scale,
            )
            return url

        return f"{url}/{path}"


class CollectionContainerListingView(ImageBankBaseView):

    def __call__(self):
        """Redirect to the @@app-image-bank"""
        return self.request.response.redirect(self.app.app_url())


class CollectionListingView(ImageBankBaseView):

    def __call__(self):
        """Redirect to the @@image-bank-collection view of the collection."""
        return self.request.response.redirect(
            f"{self.context.absolute_url()}/@@app-image-bank-collection"
        )
