import base64
import os
import tempfile
from calendar import timegm
from datetime import datetime

import jwt
import magic
from django.apps import apps
from django.conf import settings
from django.http import HttpResponse
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404
from rest_framework.reverse import reverse

from waldur_core.core import utils
from waldur_core.core.models import User
from waldur_core.structure.managers import filter_queryset_for_user


def encode_attachment_token(user_uuid, obj, field):
    max_age = settings.WALDUR_CORE['ATTACHMENT_LINK_MAX_AGE']
    dt = datetime.utcnow() + max_age
    expires_at = timegm(dt.utctimetuple())
    payload = {
        'usr': user_uuid,
        'ct': str(obj._meta),
        'id': obj.uuid.hex,
        'field': field,
        'exp': expires_at,
    }
    return utils.encode_jwt_token(payload)


def decode_attachment_token(token):
    try:
        data = utils.decode_jwt_token(token)
    except jwt.exceptions.InvalidTokenError:
        raise ValidationError('Bad signature.')

    if not isinstance(data, dict):
        raise ValidationError('Bad token data.')

    user_uuid = data.get('usr')
    content_type = data.get('ct')
    object_uuid = data.get('id')
    field = data.get('field')

    if not user_uuid:
        raise ValidationError('User UUID is not provided.')

    if not content_type:
        raise ValidationError('Content type is not provided.')

    if not object_uuid:
        raise ValidationError('Object UUID is not provided.')

    if not field:
        raise ValidationError('Field is not provided.')

    return user_uuid, content_type, object_uuid, field


def encode_protected_url(obj, field, request=None, user_uuid=None):
    """
    Returns URL with secure token for media download.
    :param obj: database model object which has file field, for example, customer or invoice.
    :param field: field name inside of database model object, for example, image or file
    :param request: authenticated HTTP request
    :param user_uuid: user UUID, it should be specified if request is not specified
    :return: URL with token.
    """
    if not user_uuid:
        user_uuid = request.user.uuid.hex
    token = encode_attachment_token(user_uuid, obj, field)
    return reverse('media-download', request=request, kwargs={'token': token})


def get_file_from_token(token):
    user_uuid, content_type, object_uuid, field = decode_attachment_token(token)
    user = get_object_or_404(User, uuid=user_uuid)
    if not user.is_active:
        raise ValidationError('User is not active.')
    queryset = apps.get_model(content_type).objects.all()
    if hasattr(queryset, 'filter_for_user'):
        queryset = queryset.filter_for_user(user)
    else:
        queryset = filter_queryset_for_user(queryset, user)
    obj = get_object_or_404(queryset, uuid=object_uuid)
    return getattr(obj, field, None)


def send_file(file):
    _, file_name = os.path.split(file.name)
    response = HttpResponse()
    response['Content-Disposition'] = 'attachment; filename=' + file_name
    response['X-Accel-Redirect'] = file.url
    return response


def dummy_image(filetype='gif'):
    """ Generate empty image in temporary file for testing """
    # 1x1px Transparent GIF
    GIF = 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
    tmp_file = tempfile.NamedTemporaryFile(suffix='.%s' % filetype)
    tmp_file.write(base64.b64decode(GIF))
    return open(tmp_file.name, 'rb')


def guess_image_extension(content: bytes) -> str:
    mime_type = magic.from_buffer(content[:1024], mime=True)
    return {
        'image/svg': 'svg',
        'image/svg+xml': 'svg',
        'image/png': 'png',
        'image/jpeg': 'jpeg',
        'image/webp': 'webp',
    }.get(mime_type)
