from django.core.exceptions import PermissionDenied, ValidationError
from django.template.defaultfilters import filesizeformat
from django.utils.translation import gettext as _
from rest_framework import viewsets
from rest_framework.response import Response

from ...acl.objectacl import add_acl_to_obj
from ...users.audittrail import create_audit_trail
from ..models import Attachment, AttachmentType
from ..serializers import AttachmentSerializer

IMAGE_EXTENSIONS = ("jpg", "jpeg", "png", "gif")


class AttachmentViewSet(viewsets.ViewSet):
    def create(self, request):
        if not request.user_acl["max_attachment_size"]:
            raise PermissionDenied(_("You don't have permission to upload new files."))

        try:
            return self.create_attachment(request)
        except ValidationError as e:
            return Response({"detail": e.args[0]}, status=400)

    def create_attachment(self, request):
        upload = request.FILES.get("upload")
        if not upload:
            raise ValidationError(_("No file has been uploaded."))

        user_roles = set(r.pk for r in request.user.get_roles())
        filetype = validate_filetype(upload, user_roles)
        validate_filesize(upload, filetype, request.user_acl["max_attachment_size"])

        attachment = Attachment(
            secret=Attachment.generate_new_secret(),
            filetype=filetype,
            size=upload.size,
            uploader=request.user,
            uploader_name=request.user.username,
            uploader_slug=request.user.slug,
            filename=upload.name,
        )

        if is_upload_image(upload):
            try:
                attachment.set_image(upload)
            except IOError:
                raise ValidationError(_("Uploaded image was corrupted or invalid."))
        else:
            attachment.set_file(upload)

        attachment.save()
        add_acl_to_obj(request.user_acl, attachment)

        create_audit_trail(request, attachment)

        return Response(
            AttachmentSerializer(attachment, context={"user": request.user}).data
        )


def validate_filetype(upload, user_roles):
    filename = upload.name.strip().lower()

    queryset = AttachmentType.objects.filter(status=AttachmentType.ENABLED)
    for filetype in queryset.prefetch_related("limit_uploads_to"):
        for extension in filetype.extensions_list:
            if filename.endswith(".%s" % extension):
                break
        else:
            continue

        if (
            filetype.mimetypes_list
            and upload.content_type not in filetype.mimetypes_list
        ):
            continue

        if filetype.limit_uploads_to.exists():
            allowed_roles = set(r.pk for r in filetype.limit_uploads_to.all())
            if not user_roles & allowed_roles:
                continue

        return filetype

    raise ValidationError(_("You can't upload files of this type."))


def validate_filesize(upload, filetype, hard_limit):
    if upload.size > hard_limit * 1024:
        message = _(
            "You can't upload files larger than %(limit)s (your file has %(upload)s)."
        )
        raise ValidationError(
            message
            % {
                "upload": filesizeformat(upload.size).rstrip(".0"),
                "limit": filesizeformat(hard_limit * 1024).rstrip(".0"),
            }
        )

    if filetype.size_limit and upload.size > filetype.size_limit * 1024:
        message = _(
            "You can't upload files of this type larger "
            "than %(limit)s (your file has %(upload)s)."
        )
        raise ValidationError(
            message
            % {
                "upload": filesizeformat(upload.size).rstrip(".0"),
                "limit": filesizeformat(filetype.size_limit * 1024).rstrip(".0"),
            }
        )


def is_upload_image(upload):
    filename = upload.name.strip().lower()

    for extension in IMAGE_EXTENSIONS:
        if filename.endswith(".%s" % extension):
            return True
    return False