from AccessControl import getSecurityManager
from AccessControl import Unauthorized
from plone import api
from plone.namedfile.utils import getAllowedSizes
from plone.protect.auto import safeWrite
from plone.rfc822.interfaces import IPrimaryFieldInfo
from ploneintranet import api as pi_api
from ploneintranet.attachments.attachments import IAttachmentStorage
from Products.CMFCore import permissions
from Products.Five.browser import BrowserView
from six.moves import urllib
from zope.annotation.interfaces import IAnnotations
from zope.datetime import rfc1123_date
from zope.interface import implementer
from zope.publisher.interfaces import IPublishTraverse

import logging

log = logging.getLogger(__name__)


@implementer(IPublishTraverse)
class Attachments(BrowserView):
    """Attachments"""

    attachment_id = None
    preview_type = None
    page = None
    scale = None

    def publishTraverse(self, request, name):
        # @@attachments/{attachment_id}[/{preview_type}]
        self.attachment_id = name

        stack = request["TraversalRequestNameStack"]
        if stack:
            self.preview_type = stack.pop()
        if stack:
            try:
                self.page = int(stack.pop())
            except ValueError:
                self.page = 1
        request["TraversalRequestNameStack"] = []
        return self

    @property
    def attachments(self):
        return IAttachmentStorage(self.context)

    @property
    def attachment(self):
        return self.attachments[self.attachment_id]

    def _prepare_imagedata(self, imgdata):
        r = self.request.RESPONSE
        r.setHeader("content-type", "image/jpeg")
        r.setHeader(
            "content-disposition",
            f'inline; filename="{self.attachment_id}_preview.jpg"',
        )
        length = len(imgdata)
        r.setHeader("content-length", length)
        return imgdata

    def _render_nopreview(self):
        primary_field = IPrimaryFieldInfo(self.attachment).value
        mimetype = primary_field.contentType
        data = primary_field.data
        # Get the actual filename from the field, since the id of the attachment
        # gets normalized. We want to return the original filename.
        filename = getattr(primary_field, "filename", self.attachment_id)
        filename = urllib.parse.quote(filename.encode("utf8"))
        if self.preview_type in ("download", "@@download"):
            content_disposition = "attachment"
        else:
            content_disposition = "inline"
        self.request.response.setHeader("content-type", mimetype)
        self.request.response.setHeader(
            "content-disposition",
            f"{content_disposition}; filename*=UTF-8''{filename}",
        )
        return data

    def render_attachment_preview(self):
        sm = getSecurityManager()
        if not sm.checkPermission(permissions.View, self.context):
            raise Unauthorized

        r = self.request.response
        attachment = self.attachment

        # avoid long dreaded CSRF error
        annotations = IAnnotations(attachment)
        if not annotations.get("ploneintranet.docconv.client", None):
            safeWrite(attachment)

        svg_preview = api.content.get_view("render.svg", attachment, self.request)
        blob, content_type = svg_preview.get_blob_and_content_type()

        if not blob:
            return
        with blob.open() as fh:
            data = fh.read()
        length = len(data)
        r.setHeader("Content-Type", content_type)
        r.setHeader("Last-Modified", rfc1123_date(self.context._p_mtime))
        r.setHeader("Accept-Ranges", "bytes")
        r.setHeader("Content-Length", length)
        return data

    def render_attachments(self):
        """replaces ploneintranet/docconv/client/view.py helpers"""
        if not self.preview_type or self.preview_type in (
            "pdf-not-available",
            "request-pdf",
            "download",
            "@@download",
        ):
            return self._render_nopreview()

        if self.preview_type == "@@images":
            images = api.content.get_view(
                "images", self.attachment.aq_base, self.request
            )
            if self.scale:
                return self._prepare_imagedata(images.scale(scale=self.scale).data.data)
            else:
                return self._render_nopreview()
        return self.render_attachment_preview()

    @property
    def allowed_scales(self):
        return getAllowedSizes().keys()

    def __call__(self):
        return self.render_attachments()


@implementer(IPublishTraverse)
class StatusAttachments(Attachments):
    """Attachments on a statusupdate"""

    status_id = None

    def publishTraverse(self, request, name):
        # @@status-attachments/{container_id}/{status_id}/{attachment_id}[/{preview_type}]
        self.container_id = int(name)
        stack = request["TraversalRequestNameStack"]
        self.status_id = int(stack.pop())
        self.attachment_id = stack.pop()
        if stack:
            self.preview_type = stack.pop()
        if stack:
            scale = stack.pop()
            self.scale = scale if scale in self.allowed_scales else "large"

        request["TraversalRequestNameStack"] = []
        return self

    @property
    def attachments(self):
        container = pi_api.microblog.get_microblog()
        # requires ViewStatusUpdate on the statusupdate returned
        try:
            statusupdate = container.get(self.status_id)
        except KeyError:
            statusupdate = container.get(self.container_id).comments[self.status_id]
        return IAttachmentStorage(statusupdate)
