import requests, os, json, time
from ftThanos.log import *


class ft_connector:
	ESC = "\x1b"
	BLACK = ESC + "[30m"
	BLUE = ESC + "[34m"
	RED = ESC + "[31m"
	GREEN = ESC + "[32m"
	DEFAULT = ESC + "[39m"
	verbose = False

	###############
	# Main fonction
	###############
	def __init__(self, **kwargs):
		env = self.dot_env_parser()
		url = 'https://api.intra.42.fr/oauth/token'
		UID = env['UID']
		SECRET = env['SECRET']
		grant_type = 'client_credentials'
		scope = "public projects profile elearning tig forum"
		try:
			reply = requests.post(url, auth=(UID, SECRET),
								  data={'scope': scope, 'grant_type': grant_type, 'client_id': UID,
										'client_secret': SECRET})
			reply.raise_for_status()
		except requests.exceptions.HTTPError as err:
			exit(critical(str(err)))
		token = json.loads(reply.text)
		self.head = {'Authorization': "Bearer {}".format(token['access_token'])}
		self.url = 'https://api.intra.42.fr/v2'
		message("42 :: Ready to use.")

	#############################################
	# Helper présentant les différentes commandes
	#############################################
	def helper(self):
		print('[42 CONNECTOR] All function available :')
		print("\t- \"launcher([requests],[url],)\" -> Will launch the request on url.")
		print("\t- \"get([url])\" -> Will return the data of the get url.")
		print("\t- \"post([url],['body'])\" -> Will post the body on the url.")
		print("\t- \"patch([url],['body'])\" -> Will patch url with the body.")
		print("\t- \"delete([url])\" -> Will delete url.")
		print(
			"\t- \"put_in_file([data],[*name])\" -> Will put the data in file, if name param was given, file will take the name, else file will be name output.json.")
		print(
			"\t- \"build_list_email([lst_login])\" -> Will build list of email (login@student.42.fr) more display all the login of given list.")
		print("\t- \"get_login([id])\" -> Return login for given id.")
		print("\t- \"get_id([login])\" -> Return id for given login.")
		print("\t- \"get_mail([login])\" -> Return email for given login.")
		print("\t- \"get_level([login])\" -> Return level for given login.")
		print("\t- \"change_password([login],[new_password])\" -> Change password for given login.")
		print("\t- \"verbose()\" -> Activate verbose.")
		print("\t- \"unverbose()\" -> Deactivate verbose.")

	################
	# Parser de .env
	################
	def dot_env_parser(self):
		"""
		Parse .env
		:param self:
		:return: return dict object with 'UID' and 'SECRET'
		"""
		ret = {}
		try:
			with open('.env', 'r') as file:
				for line in file:
					lst = line.split('=')
					if len(lst) != 2:
						continue
					ret[lst[0]] = lst[1][:-1]
		except IOError:
			exit(critical('42 ::  No .env detected.'))
		if not 'UID' in ret or not 'SECRET' in ret:
			exit(critical(
				'42 ::  Wrong parameters in .env, you should put in your file:\nSECRET=[Your secret token]\nUID=[Your UID token]'))
		return ret

	###################################
	# Créateur de .json avec de la data
	###################################
	def put_in_file(self, data, **kwargs):
		"""
		Take a dict object and put it in 42_output.json.\n
		If name of destination file given, data was put in destination file .json
		:param data: Dict object
		:param args: Name of destination file
		:return: None
		"""
		to_write = json.dumps(data, indent=4, sort_keys=True)
		if 'name' in kwargs:
			name = '42_' + kwargs['name']
		else:
			name = '42_output.json'
		if os.path.exists(name):
			os.remove(name)
		output = open(name, 'w')
		for line in to_write:
			output.write(line)
		message(f"42 ::  Data was put in {name}.")

	###################################
	# Permet de lancer un call unitaire
	###################################
	def launcher(self, ftc, url, **kwargs):
		"""
		:param ftc:
		:param url:
		:param kwargs:
		:return:
		"""
		function = self.build_function_dispatch()
		lst = url.split(' ')
		self.put_in_file(function[ftc](self.build_endpoint(lst)))

	##################################
	# Renvoie un pointeur sur fonction
	##################################
	def build_function_dispatch(self):
		"""
		:param self:
		:return:
		"""
		launch = {'SCRAP': self.scrap,
				  'GET': self.get,
				  'DEL': self.delete,
				  'POST': self.post,
				  'PATCH': self.patch
				  }
		return launch

	###########################################
	# Constuit l'url en fonction des paramètres
	###########################################
	def build_endpoint(self, param):
		"""
		:param self:
		:param param:
		:return:
		"""
		endpoint = param[0]
		for p in param[1:]:
			if '?' in endpoint:
				endpoint += "&" + p
			else:
				endpoint += "?" + p
		return endpoint

	###############################################################################################
	# Construit l'url en formatant le nombre de page et en permettant de récupérer des pages voulus
	###############################################################################################
	def build_page(self, endpoint):
		"""
		:param self:
		:param endpoint:
		:return:
		"""
		if "?" in endpoint:
			endpoint += "&"
		else:
			endpoint += "?"
		endpoint += "page[size]=100&page[number]="
		return endpoint

	###########################################
	# Scrapping de la data via get sur l'api 42
	###########################################
	def scrap(self, url):
		"""
		Scrap all the data for a given URL
		:param url: Where you want to scrap data
		:return: data
		"""
		i = 0
		sum_dict = []
		endpoint = self.build_page(url)
		while True:
			url = self.url + endpoint + str(i)
			try:
				reply = requests.get(url, headers=self.head)
				if reply.status_code == 429:
					continue
				reply.raise_for_status()
			except requests.exceptions.HTTPError as err:
				error('42 :: ' + str(err) + " on SCRAP " + endpoint)
				return
			if 'X-Page' in reply.headers:
				sum_dict += json.loads(reply.text)
				if int(reply.headers['X-Page']) * int(reply.headers['X-Per-Page']) >= int(reply.headers['X-Total']):
					break
			else:
				success("42 :: SCRAP on " + url)
				return json.loads(reply.text)
			i += 1
		success("42 :: SCRAP on " + url)
		return sum_dict

	##############################################
	# Récupération de la data via get sur l'api 42
	##############################################
	def get(self, url):
		"""
		Get data for a given URL
		:param url: Where you want to get data
		:return: data
		"""
		if "?" in url:
			url += "&page[size]=100"
		else:
			url += "?page[size]=100"
		try:
			reply = requests.get(self.url + url, headers=self.head)
			if reply.status_code == 429:
				self.get(url)
			reply.raise_for_status()
		except requests.exceptions.HTTPError as err:
			error('42 :: ' + str(err) + " on GET " + self.url + url)
			return
		success("42 :: GET on " + url)
		return json.loads(reply.text)

	######################################################
	# Post de la data sur un url en fonction du body donné
	######################################################
	def post(self, endpoint, *args):
		"""
		Post data on the endpoint
		:param endpoint: url where you want to post data
		:param args: data you want to post
		:return: None
		"""
		url = self.url + endpoint
		try:
			reply = requests.post(url, json=args[0], headers=self.head)
			reply.raise_for_status()
			success("42 :: POST on " + url)
		except requests.exceptions.HTTPError as err:
			self.put_in_file(json.loads(reply.text), name='error.json')
			error('42 :: ' + str(err) + ' on POST ' + url)

	#######################################################
	# Patch de la data sur un url en fonction du body donné
	#######################################################
	def patch(self, endpoint, *args):
		"""
		Patch data on the endpoint
		:param endpoint: url where you want to patch data
		:param args: data you want to post
		:return: None
		"""
		url = self.url + endpoint
		try:
			reply = requests.patch(url, args[0], headers=self.head, )
			reply.raise_for_status()
			success("42 :: PATCH on " + url)
		except requests.exceptions.HTTPError as err:
			self.put_in_file(json.loads(reply.text), name='error.json')
			error('42 :: ' + str(err) + ' on PATCH ' + url)

	##############################
	# Delete de la data sur un url
	##############################
	def delete(self, endpoint, **kwargs):
		"""
		Delete data on the endpoint
		:param endpoint: url where you want to delete data
		:param kwargs: data you want to delete
		:return: None
		"""
		url = self.url + endpoint
		try:
			reply = requests.delete(url, headers=self.head)
			reply.raise_for_status()
			success("42 :: DELETE on " + url)
		except requests.exceptions.HTTPError as err:
			self.put_in_file(json.loads(reply.text), name='error.json')
			error('42 :: ' + str(err) + ' on DELETE ' + url)

	###################################
	# Renvoie un login pour un id donné
	###################################
	def get_login(self, id):
		"""
		:param id: Id who you want to know associate login
		:return: Login
		"""
		url = f"/users/{id}"
		data = self.scrap(url)
		return data['login']

	###################################
	# Renvoie un id pour un login donné
	###################################
	def get_id(self, login):
		"""
		:param login: login who you want to know associate id
		:return: Id
		"""
		url = f"/users/{login}"
		data = self.scrap(url)
		return (((data['cursus_users'])[0])['user'])['id']

	#########################################################
	# Renvoie le mail privé d'un étudiant pour un login donné
	#########################################################
	def get_mail(self, login):
		"""
		:param login: login who you want to know associate private mail
		:return: private mail
		"""
		url = f"/users/{login}/user_candidature"
		data = self.scrap(url)
		return data['email']

	###########################
	# Modifie le mdp d'un login
	###########################
	def change_password(self, login, new_password):
		"""
		Change password for a given login
		:param login: Login who you want to change password
		:param new_password: new password for login
		:return: None
		"""
		url = f"/users/{login}?user[password]={new_password}"
		body = None
		self.patch(url, body=body)

	######################################
	# Renvoie le level pour un login donné
	######################################
	def get_level(self, login):
		"""
		:param login: Login who you want to know level
		:return: Level for given login
		"""
		url = f"/users/{login}/cursus_users?filter[cursus_id]=21"
		data = self.scrap(url)
		return data[0]['level']
