# API is expected to return a tuple of:
# - HTTP status code,
# - human-readable status message,
# - json with appropriate data
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from fs_scraper import scraper
from functools import wraps
from lewy_globals import getDb, colors as c
import flask, json, time
import lewy_db as ldb
import lewy_globals

def require_authentication(func):
	"""
	Ten dekorator służy do wymuszenia parametru "token"
	podczas obsługi zapytania. Powinien on zostać doklejony
	do żądania, np. /api/v1/halt?token=XXX...
	Wartość tokenu jest pobierana z pola api_key w config.toml.
	Jeżeli skrypt jej tam nie znajdzie, jest generowana losowo
	na starcie i drukowana w terminalu.
	"""
	@wraps(func)
	def wrapper(*args, **kwargs):
		token = kwargs["r"].args.get('token')
		if token == lewy_globals.config['api']['api_key']:
			try:
				status, received, data = func(*args, **kwargs)
				return status, received, data
			except:
				raise AssertionError(f"Function \"{func.__name__}\" does not return status, code, and data as it should!")
		else:
			increment_bad_requests()
			return 401, "error", {'error_msg': "Unauthorized"}
	return wrapper

def increment_bad_requests():
	"""
	Zwiększa globalny, tymczasowy licznik nieprawidłowych zapytań.
	"""
	lewy_globals.apiFailedRequests += 1

def not_implemented(data):
	"""
	Zwraca kod 501 wraz z endpointem, który wywołał błąd.
	
	:param      data:  Ścieżka zapytania
	:type       data:  list
	"""
	# TODO: change list to string -> data, not data[0]
	return 501, f"not recognised/implemented: {data[0]}", []

# GET /api/v1
def stub_hello():
	"""
	Prosta funkcja witająca użytkowników w /api/v1
	"""
	return 200, 'hello from v1! stats are at /api/v1/stats', []

def epoch_to_date(epoch):
	"""
	Zamienia Unix'owy epoch na lokalny czas,
	w formacie przypominającym format ISO.
	
	:param      epoch:  Epoch - sekundy po 1. stycznia 1970
	:type       epoch:  int
	"""
	return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(epoch))

# GET /api/v1/
# GET /api/v1/stats
def stats():
	"""
	Zwraca ogólne statystyki serwera.
	"""
	data_to_send = {
		"start_time": lewy_globals.starttime,
		"uptime": lewy_globals.getUptime(),
		"real_uptime": lewy_globals.realUptime,
		"total_api_requests": lewy_globals.apiRequests,
		"failed_api_requests": lewy_globals.apiFailedRequests,
		"outside_api_requests": lewy_globals.outsideApiHits,
		"local_api_requests": lewy_globals.apiRequests - lewy_globals.outsideApiHits
	}
	return 200, "ok", data_to_send

# GET /api/v1/matches
def get_matches(r):
	"""
	TODO: Zwraca mecze.
	"""
	response_json = []
	mecze = getDb().simple_select_all("mecze")
	for mecz in mecze:
		response_json.append(mecz.jsonify())
	print(response_json)
	return 200, "ok", response_json

# GET /api/v1/debugger_halt?token=XXX...
@require_authentication
def debugger_halt(r):
	"""
	Zatrzymuje wykonywanie skryptu, aby pozwolić
	administratorowi na wykonywanie dowolnego polecenia z konsoli.
	"""
	if lewy_globals.config['general']['is_proxied']:
		print(f"{c.WARNING}[{epoch_to_date(time.time())}]{c.ENDC} {r.headers['X-Forwarded-For']} triggered a debugger halt!")
	else:
		print(f"{c.WARNING}[{epoch_to_date(time.time())}]{c.ENDC} {r.remote_addr} triggered a debugger halt!")
	breakpoint()
	return 200, "ok", []

def last_goal_for(sportowiec: str = "MVC8zHZD"):
	"""
	Pobierz klub, dla którego sportowiec (domyślnie Robert) oddał ostatni gol.
	
	:returns:   Klub, lub None
	:rtype:     kluby | None
	"""

	sportowiec = getDb().simple_select_all("sportowcy", zewnetrzne_id_zawodnika=sportowiec)
	if sportowiec != []:
		return sportowiec[0].ostatni_gol_dla

	return None

def lookup(data, request):
	"""
	Obsługuje zapytania zwrócone do /api/v1/...
	
	:param      data:     Lista ze ścieżką zapytania
	:type       data:     list
	:param      request:  Zapytanie
	:type       request:  flask.request
	
	:returns:   Wartość zwróconą przez którąś z przywołanych funkcji.
	"""
	if data == []:
		return stub_hello()
	match data[0].lower():
		case 'stats' | '':
			return stats()
		case 'user':
			return stub_hello()
		case 'info':
			return stub_hello()
		case 'halt':
			return debugger_halt(r = request)
		case 'matches':
			return get_matches(r = request)
		case _:
			increment_bad_requests()
			return not_implemented(data)