"""
General utilities for the MPESA functions
"""

from __future__ import print_function
from .exceptions import *
from django_daraja.models import AccessToken
import requests
import json
from django.utils import timezone
from decouple import config, UndefinedValueError
import os
from requests import Response
import time
from django.conf import settings


class MpesaResponse(Response):
	response_description = ""
	error_code = None
	error_message = ''


def mpesa_response(r):
	"""
	Create MpesaResponse object from requests.Response object
	
	Arguments:
		r (requests.Response) -- The response to convert
	"""

	r.__class__ = MpesaResponse
	json_response = r.json()
	r.response_description = json_response.get('ResponseDescription', '')
	r.error_code = json_response.get('errorCode')
	r.error_message = json_response.get('errorMessage', '')
	return r


def mpesa_config(key):
	"""
	Get Mpesa configuration variable with the matching key
	
	Arguments:
		key (str) -- The configuration key

	Returns:
		str: Mpesa configuration variable with the matching key

	Raises:
		MpesaConfigurationException: Key not found
	"""

	value = getattr(settings, key, None)
	if value is None:
		try:
			value = config(key)
		except UndefinedValueError:
			# Check key in settings file
			raise MpesaConfigurationException('Mpesa environment not configured properly - ' + key + ' not found')

	return value


def api_base_url():
	"""
	Gets the base URL for making API calls

	Returns:
		The base URL depending on development environment (sandbox or production)

	Raises:
		MpesaConfigurationException: Environment not sandbox or production
	"""

	mpesa_environment = mpesa_config('MPESA_ENVIRONMENT')

	if mpesa_environment == 'sandbox':
		return 'https://sandbox.safaricom.co.ke/'
	elif mpesa_environment == 'production':
		return 'https://api.safaricom.co.ke/'
	else:
		raise MpesaConfigurationException('Mpesa environment not configured properly - MPESA_ENVIRONMENT should be sandbox or production')

def generate_access_token_request(consumer_key = None, consumer_secret = None):
	"""
	Make a call to OAuth API to generate access token
	
	Arguments:
		consumer_key (str) -- (Optional) The Consumer Key to use
		consumer_secret (str) -- (Optional) The Consumer Secret to use

	Returns:
		requests.Response: Response object with the response details

	Raises:
		MpesaConnectionError: Connection error
	"""

	url = api_base_url() + 'oauth/v1/generate?grant_type=client_credentials'
	consumer_key = consumer_key if consumer_key is not None else mpesa_config('MPESA_CONSUMER_KEY') 
	consumer_secret = consumer_secret if consumer_secret is not None else mpesa_config('MPESA_CONSUMER_SECRET')

	try:
		r = requests.get(url, auth=(consumer_key, consumer_secret))
	except requests.exceptions.ConnectionError:
		raise MpesaConnectionError('Connection failed')
	except Exception as ex:
		return ex.message
	
	return r

def generate_access_token():
	"""
	Parse generated OAuth access token, then updates database access token value

	Returns:
		AccessToken: The AccessToken object from the database

	Raises:
		MpesaError: Error generating access token
	"""

	r = generate_access_token_request()
	if r.status_code != 200:
		# Retry to generate access token
		r = generate_access_token_request()
		if r.status_code != 200:
			raise MpesaError('Unable to generate access token')

	token = r.json()['access_token']

	AccessToken.objects.all().delete()
	access_token = AccessToken.objects.create(token=token)

	return access_token

def mpesa_access_token():
	"""
	Generate access token if the current one has expired or if token is non-existent
	Otherwise return existing access token

	Returns:
		str: A valid access token
	"""

	# access_token = generate_access_token()

	access_token = AccessToken.objects.first()
	if access_token == None:
		# No access token found
		access_token = generate_access_token()
	else:
		delta = timezone.now() - access_token.created_at
		minutes = (delta.total_seconds()//60)%60
		print('minutes: ', minutes)
		if minutes > 50:
			# Access token expired
			access_token = generate_access_token()	
	
	return access_token.token

def format_phone_number(phone_number):
	"""
	Format phone number into the format 2547XXXXXXXX
	
	Arguments:
		phone_number (str) -- The phone number to format
	"""

	if len(phone_number) < 9:
		raise IllegalPhoneNumberException('Phone number too short')
	else:
		return '254' + phone_number[-9:]

def sleep(seconds, message=''):
	"""
	Sleep for the specified number of seconds
	
	Arguments:
		seconds (float) -- Number of seconds to sleep, can be a float
		message (str) -- (Optional) message to display
	"""

	print()
	print('===')
	print(message, end="")
	for i in range(seconds * 2):
		time.sleep(0.5)
		print('.', end="")
	print()
	print('===')
	print()