# coding=utf-8
"""Base classes for file download."""
from __future__ import absolute_import, print_function, unicode_literals

from datetime import datetime, timedelta

from flask import Response, request, send_file
from werkzeug.exceptions import BadRequest

from abilian.core.util import utc_dt

from .base import View


class BaseFileDownload(View):

    set_expire = False
    expire_offset = timedelta(days=365)
    as_attachment = False

    #: argument name that must be found in view kwargs. This is a safety measure
    #: to prevent setting far expire date on resources without a varying argument
    #: in url (path or query string), such as a timestamp, a serial, a hash...
    expire_vary_arg = None

    def __init__(
        self,
        set_expire=None,
        expire_offset=None,
        expire_vary_arg=None,
        as_attachment=None,
    ):
        # Override class default value only if arg is specified in constructor.
        # This allows subclasses to easily override these defaults.
        if set_expire is not None:
            self.set_expire = set_expire
        if expire_offset is not None:
            self.expire_offset = expire_offset
        if expire_vary_arg is not None:
            self.expire_vary_arg = expire_vary_arg
        if as_attachment is not None:
            self.as_attachment = bool(as_attachment)

        if self.set_expire:
            if not self.expire_offset:
                raise ValueError("expire_offset is not set")
            if not self.expire_vary_arg:
                raise ValueError("expire_vary_arg is not set")

    def prepare_args(self, args, kwargs):
        if self.set_expire:
            vary_arg = kwargs.get(
                self.expire_vary_arg, request.args.get(self.expire_vary_arg)
            )
            if vary_arg is None:
                # Argument for timestamp, serial etc is missing.
                # We must refuse to serve an image with expiry date set up
                # to maybe 1 year from now.
                # Check the code that has generated this url!
                raise BadRequest(
                    "File version marker is missing ({}=?)".format(
                        repr(self.expire_vary_arg)
                    )
                )

        args, kwargs = super(BaseFileDownload, self).prepare_args(args, kwargs)
        kwargs["attach"] = request.args.get("attach", self.as_attachment, type=bool)
        return args, kwargs

    def make_response(self, *args, **kwargs):
        # for example: return flask.make_response(...)
        # or: return flask.send_file(...)
        raise NotImplementedError()

    def get_filename(self, *args, **kwargs):
        raise NotImplementedError()

    def get_content_type(self, *args, **kwargs):
        raise NotImplementedError()

    def get(self, attach, *args, **kwargs):
        """
        :param attach: if True, return file as an attachment.
        """
        response = self.make_response(*args, **kwargs)  # type: Response
        response.content_type = self.get_content_type(*args, **kwargs)

        if attach:
            filename = self.get_filename(*args, **kwargs)
            if not filename:
                filename = "file.bin"
            headers = response.headers
            headers.add("Content-Disposition", "attachment", filename=filename)

        self.set_cache_headers(response)
        return response

    def set_cache_headers(self, response):
        if self.set_expire:
            response.cache_control.public = False
            response.cache_control.private = True
            response.cache_control.max_age = int(self.expire_offset.total_seconds())
            response.expires = utc_dt(datetime.utcnow() + self.expire_offset)


class BaseBlobDownload(BaseFileDownload):
    def get_blob(self, *args, **kwargs):
        raise NotImplementedError()

    def prepare_args(self, args, kwargs):
        args, kwargs = super(BaseBlobDownload, self).prepare_args(args, kwargs)
        self.blob = self.get_blob(*args, **kwargs)
        metadata = self.blob.meta
        self.filename = metadata.get("filename", self.obj.name)
        self.content_type = metadata.get("mimetype")
        return args, kwargs

    def get_filename(self, *args, **kwargs):
        return self.filename

    def get_content_type(self, *args, **kwargs):
        return self.content_type

    def make_response(self, *args, **kwargs):
        blob = self.blob
        stream = blob.file.open("rb")

        return send_file(
            stream, mimetype=self.content_type, cache_timeout=0, add_etags=False
        )
