from datetime import datetime
from logging import getLogger
from plone import api
from ploneintranet.docconv.client.browser.svg import preferred_basenames
from ploneintranet.docconv.client.interfaces import IPreviewSettings
from ploneintranet.docconv.client.utils import getDocumentType
from ploneintranet.docconv.client.utils import PDF_FILENAME
from urllib.parse import urlencode
from zope.annotation import IAnnotations

logger = getLogger(__name__)

PREVIEW_URL = (
    "++theme++ploneintranet.theme/generated/media/logos/plone-intranet-square.svg"
)


def converting(obj):
    """Check if object is currently being converted

    :param obj: The Plone object to get previews for
    :type obj: A Plone content object
    :return: True if converting, False if no longer converting
    :rtype: boolean
    """
    settings = IPreviewSettings(obj)
    return settings.converting


def successfully_converted(obj):
    """Check if object could be converted

    :param obj: The Plone object to get previews for
    :type obj: A Plone content object
    :return: True if successfully converted, False if conversion failed
    :rtype: boolean
    """

    # New style way
    settings = IPreviewSettings(obj)
    return settings.successfully_converted


def has_previews(obj):
    """Test if the object has a pdf so that it can generate previews.

    :param obj: The Plone object to get previews for
    :type obj: A Plone content object
    :return: True if there can be previews. False otherwise.
    :rtype: boolean
    """
    doc_type = getDocumentType(obj)
    if not doc_type:
        return False
    if doc_type.requires_conversion is False:
        # This is already a pdf, yes, we can have previews!
        return True

    # This is a supported filetype other than pdf. Check if the pdf has already
    # been generated.
    # Note that Images are supported but will not get a pdf file, so this
    # called on images would return False.
    blob_files = IPreviewSettings(obj).blob_files or {}
    return (
        PDF_FILENAME in blob_files
        or "original_svg_1.svg" in blob_files
        or "original_png_1.svg" in blob_files
        or "1200_png_1.svg" in blob_files
        or "300_png_1.svg" in blob_files
        or "dump_1.gif" in blob_files
    )


def get_preview_urls(obj, with_timestamp=False, small=True):
    """Convenience method to get URLs of image previews as these are most
    frequently used

    :param obj: The Plone content object to get preview URLs for
    :type obj: A Plone content object
    :param with_timestamp: If True add a timestamp to the URLs
    :type with_timestamp: bool
    :param small: Whether to return small (True), or large (False) previews
    :type small: Bool
    :return: List of preview image absolute URLs
    :rtype: list
    """

    settings = IPreviewSettings(obj)
    if not settings or (settings.blob_files is None):
        return [fallback_image_url(obj)]

    number_of_previews = settings.num_pages or 0
    # If there aren't any previews, return the placeholder url
    if number_of_previews < 1:
        return [fallback_image_url(obj)]
    urls = (
        f"{obj.absolute_url()}/@@render.svg?page={page}"
        for page in range(1, number_of_previews + 1)
    )
    if not small:
        urls = (f"{url}&size=gallery" for url in urls)

    if with_timestamp:
        try:
            timestamp = settings.last_updated
        except AttributeError:
            logger.error("Cannot get timestamp from %r", obj)
            timestamp = datetime.now().isoformat()
        qs = "&%s" % urlencode({"t": timestamp})
        urls = ((url + qs) for url in urls)
    return list(urls)


def fallback_image(obj):
    """Return a fallback image for use if there are no previews.

    :param obj: The Plone object to get previews for
    :type obj: A Plone content object
    :return: An image object
    :rtype: ZODB.blob.Blob
    """
    return api.portal.get().restrictedTraverse(PREVIEW_URL)


def fallback_image_url(obj):
    """Return a fallback image URL for use if there are no previews.

    :param obj: The Plone object to get previews for
    :type obj: A Plone content object
    :return: An image object url
    :rtype: string
    """
    return f"{api.portal.get().absolute_url()}/{PREVIEW_URL}"


# FIXME this can be removed, is not actually used anywhere
def get_thumbnail(obj):
    """Get the thumnail preview image for the given object

    :param obj: The Plone object to get previews for
    :type obj: A Plone content object
    :return: The preview thumbnail
    :rtype: ImageScaling
    """
    blob_files = IPreviewSettings(obj).blob_files
    if blob_files is not None:
        previews = list(blob_files.keys())
        for basename in preferred_basenames():
            filename = f"{basename}_1.svg"
            if filename in previews:
                return blob_files[filename]
    return fallback_image(obj)


def has_pdf(obj):
    """Returns true if a pdf has been generated.

    :param obj: The Plone content object to get preview URLs for
    :type obj: A Plone content object
    :return: True if there is a pdf available
    :rtype: boolean
    """
    # No need to generate a pdf if the object already is a pdf
    if (
        hasattr(obj, "content_type")
        and callable(obj.content_type)
        and obj.content_type() == "application/pdf"
    ):
        return True

    blob_files = IPreviewSettings(obj).blob_files or {}
    return PDF_FILENAME in blob_files


def get_pdf(obj):
    """Return the generated pdf.

    :param obj: The Plone content object to get preview URLs for
    :type obj: A Plone content object
    :return: The generated pdf preview of the content item
    :rtype: Blob
    """
    blob_files = IPreviewSettings(obj).blob_files or {}
    return blob_files.get(PDF_FILENAME)


def purge_previews(obj):
    """
    Purge the previews from obj

    :param obj: The Plone content object for which we want to purge
                the previews
    :type obj: A Plone content object
    :return: Does not return anything.
    :rtype: None
    """
    IAnnotations(obj).pop("ploneintranet.docconv.client", {})
    IPreviewSettings(obj)


def num_pages(obj):
    """Returns the number of pages found on the generated pdf file"""
    return IPreviewSettings(obj).num_pages
