"""This module contains the custom fields for the ``media`` app"""

import mimetypes

from django.core import checks
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.template.defaultfilters import filesizeformat


class MimeTypeRestrictedFileField(models.FileField):
    """A FileField accepting files based on a set of provided mimetypes

    We expect the following parameters to be part of the ``kwargs``:
    mime_types: list(str)
        A list of allowed mime types for the uploaded file
        ex: ['video/mp4', 'video/x-msvideo']
    max_size: int
        The maximum size of the uploaded file (in octets)
        ex: 104857600 for 100MB

    For the parameters, see the ``models.FileField``

    Examples
    --------
    >>> from django.db import models
    >>> from mtp.apps.media.fields import MimeTypeRestrictedFileField
    ...
    >>> class MediaModelWithPdfField(models):
    ...     file = MimeTypeRestrictedFileField(
    ...         upload_to='media/',
    ...         verbose_name=_('Pdf file'),
    ...         mime_types=['application/pdf'],
    ...         max_size=104857600,  # 100MB
    ...     )

    """

    def __init__(self, mime_types=None, max_size=None, **kwargs):
        """Store the provided list of accepted mimetypes and max_size

        For the parameters, see ``django.db.models.FileField``

        """

        super().__init__(**kwargs)

        self.mime_types = mime_types
        self.max_size = max_size

    def check(self, **kwargs):
        """Check that the field is correctly configured

        For the parameters, see ``django.db.models.FileField``

        Returns
        -------
        list

        """

        errors = super().check(**kwargs)

        errors.extend(self._check_mime_types_and_max_size())

        return errors

    def _check_mime_types_and_max_size(self):
        """Check that the mime_types and max_size parameters are provided

        Returns
        -------
        list

        """

        errors = []

        if not self.mime_types:
            errors.append(
                checks.Error(
                    '"mime_types" parameter not provided or None',
                    hint='Provide a value for the "mime_types" parameter',
                    obj=self,
                    id='media.E001',
                )
            )

        if not self.max_size:
            errors.append(
                checks.Error(
                    '"max_size" parameter not provided or None',
                    hint='Provide a value for the "max_size" parameter',
                    obj=self,
                    id='media.E002',
                )
            )

        return errors

    def clean(self, *args, **kwargs):
        """Restrict the mimetype and the max size of the uploaded file

        For the parameters, see ``django.db.models.FileField``

        Raises
        ------
        ValidationError
            If the mimetype of the uploaded file does not match one of our ``self.mimetypes``.
            If the size of the uploaded file is greater than our ``self.max_size``.

        """

        field = super().clean(*args, **kwargs)

        file = field.file

        # Check the mime type
        mimetype, __ = mimetypes.guess_type(file.name)
        if mimetype not in self.mime_types:
            raise ValidationError(
                _('Unsupported file type. Please upload one of the following: %(file_types)s') % {
                    'file_types': ', '.join(self.mime_types)
                }
            )

        # Check the file size
        file_size = file.size
        if file_size > self.max_size:
            raise ValidationError(
                _('Please keep file size under %(max_size)s. Current size is %(current_size)s') % {
                    'max_size': filesizeformat(self.max_size),
                    'current_size': filesizeformat(file_size)
                }
            )

        return field
