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

from datetime import datetime, timedelta

from flask import 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 theses 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 image: image as bytes
    :param s: requested maximum width/height size
    """
    response = self.make_response(*args, **kwargs)
    response.headers['content-type'] = self.get_content_type(*args, **kwargs)

    if attach:
      filename = self.get_filename(*args, **kwargs)
      if not filename:
        filename = u'file.bin'
      response.headers.add('Content-Disposition', u'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,
                     as_attachment=False,
                     mimetype=self.content_type,
                     cache_timeout=0,
                     add_etags=False,
                     conditional=False)
