# technical
import sys
import os
from threading import Thread
from importlib import resources
import datauri
import time


# server stuff
from bottle import Bottle, static_file, request, response, FormsDict, redirect, BaseRequest, abort
import waitress
from jinja2.exceptions import TemplateNotFound

# doreah toolkit
from doreah.logging import log
from doreah import auth

# rest of the project
from . import database
from .database.jinjaview import JinjaDBConnection
from .images import resolve_track_image, resolve_artist_image
from .malojauri import uri_to_internal, remove_identical
from .pkg_global.conf import malojaconfig, data_dir
from .jinjaenv.context import jinja_environment
from .apis import init_apis, apikeystore


from .dev.profiler import profile


######
### TECHNICAL SETTINGS
#####

PORT = malojaconfig["PORT"]
HOST = malojaconfig["HOST"]
THREADS = 16
BaseRequest.MEMFILE_MAX = 15 * 1024 * 1024

#STATICFOLDER = importlib.resources.path(__name__,"web/static")

webserver = Bottle()




######
### ERRORS
#####


@webserver.error(400)
@webserver.error(403)
@webserver.error(404)
@webserver.error(405)
@webserver.error(408)
@webserver.error(500)
@webserver.error(503)
@webserver.error(505)
def customerror(error):
	error_code = error.status_code
	error_desc = error.status
	traceback = error.traceback
	body = error.body or ""
	traceback = traceback.strip() if traceback is not None else "No Traceback"
	adminmode = request.cookies.get("adminmode") == "true" and auth.check(request)

	template = jinja_environment.get_template('error.jinja')
	return template.render(
		error_code=error_code,
		error_desc=error_desc,
		traceback=traceback,
		error_full_desc=body,
		adminmode=adminmode,
	)










######
### REGISTERING ENDPOINTS
#####

aliases = {
	"admin": "admin_overview",
	"manual": "admin_manual",
	"setup": "admin_setup",
	"issues": "admin_issues"
}


### API

auth.authapi.mount(server=webserver)
init_apis(webserver)

# redirects for backwards compatibility
@webserver.get("/api/s/<pth:path>")
@webserver.post("/api/s/<pth:path>")
def deprecated_api_s(pth):
	redirect("/apis/" + pth + "?" + request.query_string,308)

@webserver.get("/api/<pth:path>")
@webserver.post("/api/<pth:path>")
def deprecated_api(pth):
	redirect("/apis/mlj_1/" + pth + "?" + request.query_string,308)




### STATIC

@webserver.route("/image")
def dynamic_image():
	keys = FormsDict.decode(request.query)
	if keys['type'] == 'track':
		result = resolve_track_image(keys['id'])
	elif keys['type'] == 'artist':
		result = resolve_artist_image(keys['id'])

	if result is None or result['value'] in [None,'']:
		return ""
	if result['type'] == 'raw':
		# data uris are directly served as image because a redirect to a data uri
		# doesnt work
		duri = datauri.DataURI(result['value'])
		response.content_type = duri.mimetype
		return duri.data
	if result['type'] == 'url':
		redirect(result['value'],307)

@webserver.route("/images/<pth:re:.*\\.jpeg>")
@webserver.route("/images/<pth:re:.*\\.jpg>")
@webserver.route("/images/<pth:re:.*\\.png>")
@webserver.route("/images/<pth:re:.*\\.gif>")
def static_image(pth):

	ext = pth.split(".")[-1]
	small_pth = pth + "-small"
	if os.path.exists(data_dir['images'](small_pth)):
		resp = static_file(small_pth,root=data_dir['images']())
	else:
		try:
			from pyvips import Image
			thumb = Image.thumbnail(data_dir['images'](pth),300)
			thumb.webpsave(data_dir['images'](small_pth))
			resp = static_file(small_pth,root=data_dir['images']())
		except Exception:
			resp = static_file(pth,root=data_dir['images']())

	#response = static_file("images/" + pth,root="")
	resp.set_header("Cache-Control", "public, max-age=86400")
	resp.set_header("Content-Type", "image/" + ext)
	return resp


@webserver.route("/login")
def login():
	return auth.get_login_page()

# old
@webserver.route("/<name>.<ext>")
@webserver.route("/media/<name>.<ext>")
def static(name,ext):
	assert ext in ["txt","ico","jpeg","jpg","png","less","js","ttf","css"]
	with resources.files('maloja') / 'web' / 'static' as staticfolder:
		response = static_file(ext + "/" + name + "." + ext,root=staticfolder)
	response.set_header("Cache-Control", "public, max-age=3600")
	return response

# new, direct reference
@webserver.route("/static/<path:path>")
def static(path):
	with resources.files('maloja') / 'web' / 'static' as staticfolder:
		response = static_file(path,root=staticfolder)
	response.set_header("Cache-Control", "public, max-age=3600")
	return response

# static files not supplied by the package
@webserver.get("/static_custom/<category>/<path:path>")
def static_custom(category,path):
	rootpath = {
		'css':data_dir['css']()
	}
	response = static_file(path,root=rootpath[category])
	response.set_header("Cache-Control", "public, max-age=3600")
	return response


### DYNAMIC

@profile
def jinja_page(name):
	if name in aliases: redirect(aliases[name])
	keys = remove_identical(FormsDict.decode(request.query))

	adminmode = request.cookies.get("adminmode") == "true" and auth.check(request)

	with JinjaDBConnection() as conn:

		loc_context = {
			"dbc":conn,
			"adminmode":adminmode,
			"apikey":request.cookies.get("apikey") if adminmode else None,
			"apikeys":apikeystore,
			"_urikeys":keys, #temporary!
		}
		loc_context["filterkeys"], loc_context["limitkeys"], loc_context["delimitkeys"], loc_context["amountkeys"], loc_context["specialkeys"] = uri_to_internal(keys)
		try:
			template = jinja_environment.get_template(name + '.jinja')
			res = template.render(**loc_context)
		except TemplateNotFound:
			abort(404,f"Not found: '{name}'")
		except (ValueError, IndexError):
			abort(404,"This Artist or Track does not exist")

	if malojaconfig["DEV_MODE"]: jinja_environment.cache.clear()

	return res

@webserver.route("/<name:re:admin.*>")
@auth.authenticated
def jinja_page_private(name):
	return jinja_page(name)

@webserver.route("/<name>")
def jinja_page_public(name):
	return jinja_page(name)

@webserver.route("")
@webserver.route("/")
def mainpage():
	return jinja_page("start")


# Shortlinks

@webserver.get("/artist/<artist>")
def redirect_artist(artist):
	redirect("/artist?artist=" + artist)
@webserver.get("/track/<artists:path>/<title>")
def redirect_track(artists,title):
	redirect("/track?title=" + title + "&" + "&".join("artist=" + artist for artist in artists.split("/")))




######
### RUNNING THE SERVER
#####


# warning interception
import logging

class WaitressLogHandler():
	def __init__(self):
		self.lastwarned = 0
		self.barrier = 5
		self.level = 20
		self.filters = []
	def handle(self,record):
		if record.name == 'waitress.queue':
			now = time.time()
			depth = record.args[0]

			if depth > self.barrier:
				log(f"Waitress Task Queue Depth at {depth}")
				self.lastwarned = now
				self.barrier = max(depth,self.barrier+5)
			elif now - self.lastwarned > 5:
				self.barrier = max(5,self.barrier-5)
		else:
			log(f"Waitress: {record.msg % record.args}")
logging.getLogger().addHandler(WaitressLogHandler())


def run_server():
	log("Starting up Maloja server...")

	## start database
	Thread(target=database.start_db).start()



	try:
		#run(webserver, host=HOST, port=MAIN_PORT, server='waitress')
		listen = f"{HOST}:{PORT}"
		log(f"Listening on {listen}")
		waitress.serve(webserver, listen=listen, threads=THREADS)
	except OSError:
		log("Error. Is another Maloja process already running?")
		raise
