"""Stream files that live in models."""
from django.views.generic.detail import SingleObjectMixin

from django_downloadview.exceptions import FileNotFound
from django_downloadview.views.base import BaseDownloadView


class ObjectDownloadView(SingleObjectMixin, BaseDownloadView):
    """Serve file fields from models.

    This class extends :class:`~django.views.generic.detail.SingleObjectMixin`,
    so you can use its arguments to target the instance to operate on:
    ``slug``, ``slug_kwarg``, ``model``, ``queryset``...

    In addition to :class:`~django.views.generic.detail.SingleObjectMixin`
    arguments, you can set arguments related to the file to be downloaded:

    * :attr:`file_field`;
    * :attr:`basename_field`;
    * :attr:`encoding_field`;
    * :attr:`mime_type_field`;
    * :attr:`charset_field`;
    * :attr:`modification_time_field`;
    * :attr:`size_field`.

    :attr:`file_field` is the main one. Other arguments are provided for
    convenience, in case your model holds some (deserialized) metadata about
    the file, such as its basename, its modification time, its MIME type...
    These fields may be particularly handy if your file storage is not the
    local filesystem.

    """

    #: Name of the model's attribute which contains the file to be streamed.
    #: Typically the name of a FileField.
    file_field = "file"

    #: Optional name of the model's attribute which contains the basename.
    basename_field = None

    #: Optional name of the model's attribute which contains the encoding.
    encoding_field = None

    #: Optional name of the model's attribute which contains the MIME type.
    mime_type_field = None

    #: Optional name of the model's attribute which contains the charset.
    charset_field = None

    #: Optional name of the model's attribute which contains the modification
    # time.
    modification_time_field = None

    #: Optional name of the model's attribute which contains the size.
    size_field = None

    def get_file(self):
        """Return :class:`~django.db.models.fields.files.FieldFile` instance.

        The file wrapper is model's field specified as :attr:`file_field`. It
        is typically a :class:`~django.db.models.fields.files.FieldFile` or
        subclass.

        Raises :class:`~django_downloadview.exceptions.FileNotFound` if
        instance's field is empty.

        Additional attributes are set on the file wrapper if :attr:`encoding`,
        :attr:`mime_type`, :attr:`charset`, :attr:`modification_time` or
        :attr:`size` are configured.

        """
        file_instance = getattr(self.object, self.file_field)
        if not file_instance:
            raise FileNotFound(
                f'Field="{self.file_field}" on object="{self.object}" is empty'
            )
        for field in ("encoding", "mime_type", "charset", "modification_time", "size"):
            model_field = getattr(self, "%s_field" % field, False)
            if model_field:
                value = getattr(self.object, model_field)
                setattr(file_instance, field, value)
        return file_instance

    def get_basename(self):
        """Return client-side filename."""
        basename = super(ObjectDownloadView, self).get_basename()
        if basename is None:
            field = "basename"
            model_field = getattr(self, "%s_field" % field, False)
            if model_field:
                basename = getattr(self.object, model_field)
        return basename

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(ObjectDownloadView, self).get(request, *args, **kwargs)
