# import sys
#sys.path.append("/var/www/mission/venv/lib/python3.11/site-packages/")

from typing import Optional,Any,TypeAlias
from flask import Flask,jsonify,Response,session,request,make_response,g,send_from_directory
from werkzeug.exceptions import HTTPException
from werkzeug.debug import DebuggedApplication
from flask.logging import default_handler
from functools import wraps
import logging
import Utils
from Utils import toInt as i,toList as l,toStr as s,f,getUserID,getIP,getUserName
import time
from Utils import Debug

class RequestFormatter(logging.Formatter):
	def format(self, record):
		if request:
			record.uri = request.base_url
			record.path = request.full_path
		else:
			record.uri = None
			record.path = None
		return super().format(record)

app = Flask(__name__)
app.secret_key = "31337"
###################################
## Debug Mode
app.debug = True
app.config['debug'] = True
app.config['PROPAGATE_EXCEPTIONS'] = True

from flask.json.provider import _default as _json_default
import app as app_cls
import datetime
import types
def json_default(o):
	match type(o):
		case app_cls.Geo:
			if o is not None:
				return str(o)
			else:
				return ""
		case types.GeneratorType:
			return list(o)
		case datetime.datetime:
			return o.isoformat()
		case Utils.IODate:
			return o.full()
		case _:
			return _json_default(o)

app.json.default = json_default


# import time

# START_TIME = time.perf_counter()

###################################
## Setup Logging to file

file_log = logging.FileHandler('api.log',encoding='utf8')
formatter = RequestFormatter('', datefmt="%d/%m/%Y %I:%M:%S %p")
file_log.setFormatter(formatter)
wlog = logging.getLogger('werkzeug')
wlog.setLevel(logging.INFO)

wlog.addHandler(file_log)
# app.logger.setLevel(logging.INFO)
app.logger.addHandler(file_log)
# app.logger.basicConfig(level=logging.DEBUG,stream=sys.stderr)

###################################
## Set app to application for wsgi

io_response:TypeAlias =tuple[dict,int]

try:
	application = DebuggedApplication(app, pin_logging=True,console_path="/console")
	#application = app
except Exception as e:
	Utils.Debug("App Trouble",data=e)

@app.before_request
def before_request():
	g.start_time = time.time()

@app.after_request
def after_request(response):
	if hasattr(g, 'start_time'):
		et = time.time() - g.start_time
		print_request(et)
	return response
# application = app
###################################
## Routes

def Output(data:Optional[Any]=None,status:int = 1,message:str = "",error:Optional[str|Exception]=None,code=200) -> tuple[Any,int]:
	if not data or isinstance(data,bool):
		data = {}
	if error:
		Utils.Debug(title="Error",data=error)
		if data:
			return(data,code)
		else:
			return(Utils.errorStr(error),500)
	if status == -3:
		app.logger.error(f'{message} - {status} - {code}')
		return ("",code)

	return (data,200)


class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

def print_request(et):
	# global START_TIME
	dt = Utils.IODate()
	# END_TIME = time.perf_counter()
	# app.logger.warning(START_TIME)
	# secs = (END_TIME - START_TIME)
	# app.logger.warning(f"Secs {secs}")
	# ms = secs * 1000 if secs > 1 else secs * 1000
	ms = et * 1000
	app.logger.warning(f"[IO] {dt.full()} | {bcolors.OKBLUE} {ms}ms {bcolors.ENDC}| {bcolors.OKGREEN} {request.path} {bcolors.ENDC} |")

def auth(perms:list):
	def do_auth(func):
		@wraps(func)
		def wrapper(*args,**kwargs):
			check=""
			parms = Utils.parms()
			if parms.get("u") and parms.get("p"):
				login()
			user_groups = session.get("User",{}).get("Groups") or []
			for check in perms:
				if check in user_groups:
					return func(*args,**kwargs)

			return Output(data=None,status=-3,message=f"Not authorized for {check} {parms}",code=401)

		return wrapper
	return do_auth


@app.route('/js/<path:filename>')
def serve_js(filename):
    return send_from_directory('js', filename)

@app.route('/css/<path:filename>')
def serve_css(filename):
    return send_from_directory('css', filename)

@app.route("/json")
def test_json() -> dict:
	data = {
		"columns":[
			{"title":"Age","field":"age"},
			{"title":"First Name","field":"first_name"},
			{"title":"Last Name","field":"last_name"}
		],
		"rows":[
			{ "age": 40, "first_name": 'Bob', "last_name": 'Jones' }
		]
	}
	return data

@app.route("/")
def io() -> io_response:
	return Output(data={"data":"Words"},status=1,message="Success")

@app.route("/admin/login",methods=["POST"])
def admlogin():
	from app import Users
	parms = Utils.parms()
	user = ""
	try:
		assert len(parms.get("u","")) > 0 and len(parms.get("p",""))>0, "Invalid user/pass"
		user = Users().auth(user=parms.get("u",""),password=parms.get("p",""))
		assert user, "No user"
		session["User"] = user
		return Output(data={},status=1,message="Login Successful")
	except Exception as e:
		Debug(title="Auth Fail",data=[parms])
		session["User"] = {}
		return Output(data="Login Failed",status=0,code=401,error=f"User or Password is invalid {Utils.toJSON(user)}")

@app.route("/login",methods=["POST"])
def login():
	from app import Users
	parms = Utils.parms()
	user = Users().get_userid(user_id=parms.get("auth",""))
	# Debug("Auth Check Result",data=user);
	if user:
		user["Groups"] = ["User"]
		session["User"] = user
		return Output(data={},status=1,message="Login Successful")
	else:
		Debug(title="Auth Fail",data=[parms])
		session["User"] = {}
		return Output(data="Login Failed",status=0,code=401,error=f"User or Password is invalid {Utils.toJSON(user)}")

@app.route("/logout",methods=["GET"])
def logout():
	data = {"User":{}}
	session["User"] = {}
	resp = make_response(Output(data={"User":session.get("User")},status=1,message="Logout Successful"))
	return resp

@app.errorhandler(Exception)
def server_error(err) -> io_response:
	app.logger.exception(err)
	print("Server Error")
	Utils.Debug(title="Trouble",data=err)
	return Output(data={},status=0,message=f"exception {err}")

@app.errorhandler(404)
def page_not_found(error) -> io_response:
	return Output(data={},status=0,error=f"This page does not exist {request.path}",code=404)

@app.errorhandler(500)
def server_barf(error) -> io_response:
	Utils.Debug(title="Server Crash",data=error)
	return Output(data={},status=0,error=str(error),code=200)

@app.errorhandler(HTTPException)
def handle_exception(e) -> Response:
	import json
	response = e.get_response()
	response.data = json.dumps({
		"code": e.code,
		"name": e.name,
		"status":0,
		"description": e.description,
	})
	response.content_type = "application/json"
	Utils.Debug(title="HTTP ERROR",data=response.data)

	return response

