import time
import jwt
import six
from django.contrib.auth import get_user_model
from django.conf import settings
from .utils import parse_last_event_id, get_channelmanager

try:
	from urllib import unquote
except ImportError:
	from urllib.parse import unquote

class EventRequest(object):
	class Error(ValueError):
		pass

	class GripError(Error):
		pass

	class ResumeNotAllowedError(Error):
		pass

	def __init__(self, http_request=None, channel_limit=10, view_kwargs=None):
		if view_kwargs is None:
			view_kwargs = {}

		self.channels = set()
		self.channel_last_ids = {}
		self.is_recover = False
		self.user = None

		if http_request:
			self.apply_http_request(http_request,
				channel_limit=channel_limit,
				view_kwargs=view_kwargs)

	def apply_http_request(self, http_request, channel_limit, view_kwargs):
		is_next = False
		is_recover = False
		user = None

		es_meta = {}
		if http_request.GET.get('es-meta'):
			es_meta = jwt.decode(http_request.GET['es-meta'], settings.SECRET_KEY.encode('utf-8'), algorithms=['HS256'])
			if int(time.time()) >= es_meta['exp']:
				raise ValueError('es-meta signature is expired')

		if 'user' in es_meta:
			if es_meta['user'] != 'anonymous':
				user = get_user_model().objects.get(pk=es_meta['user'])
		else:
			if hasattr(http_request, 'user') and http_request.user.is_authenticated:
				user = http_request.user

		if 'channels' in es_meta:
			channels = es_meta['channels']
		else:
			channelmanager = get_channelmanager()
			channels = channelmanager.get_channels_for_request(http_request, view_kwargs)

		if len(channels) < 1:
			raise EventRequest.Error('No channels specified')

		if len(channels) > channel_limit:
			raise EventRequest.Error('Channel limit exceeded')

		if http_request.GET.get('link') == 'next':
			is_next = True

		if http_request.GET.get('recover') == 'true':
			channel_last_ids = {}
			is_recover = True
			for grip_channel, last_id in six.iteritems(http_request.grip.last):
				if not grip_channel.startswith('events-'):
					continue
				channel = unquote(grip_channel[7:])
				if channel in channels:
					channel_last_ids[channel] = last_id
		else:
			last_event_id = http_request.META.get('HTTP_LAST_EVENT_ID')
			if not last_event_id:
				# take the first non-empty param, from the end
				for val in reversed(http_request.GET.getlist('lastEventId')):
					if val:
						last_event_id = val
						break

			if last_event_id:
				if last_event_id == 'error':
					raise EventRequest.ResumeNotAllowedError(
						'Can\'t resume session after stream-error')

				try:
					parsed = parse_last_event_id(last_event_id)

					channel_last_ids = {}
					for channel, last_id in six.iteritems(parsed):
						channel = unquote(channel)
						if channel in channels:
							channel_last_ids[channel] = last_id
				except:
					raise EventRequest.Error(
						'Failed to parse Last-Event-ID or lastEventId')
			else:
				channel_last_ids = {}

		self.channels = channels
		self.channel_last_ids = channel_last_ids
		self.is_next = is_next
		self.is_recover = is_recover
		self.user = user
