import sys
sys.path.append("/var/www/passport.vulcan-creative.com/venv/lib/python3.11/site-packages/")

from typing import Optional,Any
from flask import Flask,jsonify,Response,session,request
from werkzeug.exceptions import HTTPException
from werkzeug.debug import DebuggedApplication
from flask.logging import default_handler
import logging
import Utils
import os
import random
from Utils import toInt as i,toList as l,toStr as s
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

		# if record.levelno == logging.ERROR:
		# 	record = f"\x1b[31;20m"{record}"\x1b[0m"
		# elif record.levelno == logging.INFO:
		# 	record = f"\x1b[33;20m"{record}"\x1b[0m"
		return super().format(record)

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

###################################
## 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.DEBUG)
wlog.addHandler(file_log)
app.logger.setLevel(logging.DEBUG)
app.logger.addHandler(file_log)
logging.basicConfig(level=logging.DEBUG,stream=sys.stderr)

###################################
## Set app to application for wsgi
application = DebuggedApplication(app, pin_logging=True,console_path="/console")
# application = app
###################################
## Routes

def save_file(f) -> str:
	fname = ""
	try:
		assert f is not None, "No File"
		content_dir = "/var/www/passport.vulcan-creative.com/site/content"
		fname = f.filename

		base, ext = os.path.splitext(fname)
		while os.path.exists(os.path.join(content_dir, fname)):
			fname = f"{base}_{random.randint(1, 1_000_000)}{ext}"

		f.save(os.path.join(content_dir, fname))
	except:
		pass

	return fname
def render_select(items:list,selected:Any):
	s = ""
	for item in items:
		if item.get("ID") == i(selected) or item.get("Name") == selected:
			s+=f'<option value="{item.get("ID")}" selected>{item.get("Name")}</option>'
		else:
			s+=f'<option value="{item.get("ID")}">{item.get("Name")}</option>'
	return s
try:
	@app.route("/refresh")
	def refresh() -> dict:
		from pathlib import Path
		status = Path('Service.py').touch()		
		o = Output(data={"response":status},status=1,message="Success")
		return o

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

	@app.route("/checkin/",methods=["GET","POST"])
	def checkin() -> dict:
		import json
		from db import Explorers,Locations
		parms = Utils.parms()
		d_id = Utils.url_decode(str(parms.get("d_id")))
		d_parms = json.loads(Utils.decr(data=d_id))
		l_id = i(d_parms.get("loc_id"))
		email = s(parms.get("email"))
		geo = d_parms.get("geo")
		explorers = Explorers()
		status = explorers.checkin(email=email,location_id=l_id,geo=geo)
		loc = Locations().get(location_id=l_id)
		event_id = i(loc.get("Event_ID"))
		log_book = Explorers().log_book(email=email,event_id=event_id)
		locs = list(Locations().get_event(event_id=event_id))
		log = Utils.group_by("ID",log_book)
		for idx in range(len(locs)):
			if log.get(locs[idx].get("ID")):
				locs[idx]["Timestamp"] = log.get(locs[idx].get("ID")).get("Timestamp")
			else:
				locs[idx]["Timestamp"] = None

		return Output(data=locs,status=1 if status else 0)

	@app.route("/event_detail/",methods=["POST"])
	def detail() -> dict:
		from db import Events,Explorers
		import json
		try:
			parms = Utils.parms()
			d_id = str(parms.get("d_id"))
			d_id = Utils.url_decode(d_id)
			d_parms = json.loads(Utils.decr(data=str(d_id)))
			l_id = d_parms.get("loc_id")
			data = Events().detail(loc_id=l_id)
			return Output(data=data,error="Location Not Found" if not data else None)
		except Exception as e:
			return Output(data={},error=str(e))

	@app.route("/adm/dash/")
	def adm_dash() -> dict:
		from db import Events,Locations,Explorers
		data = {
			"Event":{},
			"Events":Events().all(),
			"Locations":Locations().all(),
			"Explorers":Explorers().all()
		}
		return Output(data)

	@app.route("/adm/events/urls/<id>")
	def adm_event_urls(id:int) -> list:
		from db import Locations
		d = Utils.mk_csv(Locations().url_list(id))
		Utils.Debug(title="Data",data=d)
		response = Response(d, content_type="text/csv")
		# response.headers["Content-Disposition"] = "attachment; filename=data.csv"
		return response

	###################################
	## Events
	@app.route("/adm/events/")
	def adm_events() -> dict:
		from db import Events
		return Output(data=Events().all())

	@app.route("/adm/events/<id>")
	def adm_events_get(id:int) -> dict:
		from db import Events,Locations
		data = Events().overview(id)
		locs = Locations()
		try:
			for i in range(len(data.get("Locations",[]))):
				data["Locations"][i]["test_url"] = locs.get_url(data.get("Locations",[])[i].get("ID"))
		except Exception as e:
			Utils.Debug(title="Trouble with events all",data=e)

		return Output(data=data)

	@app.route("/adm/events/select/")
	def adm_events_select() -> dict:
		from db import Events
		parms = Utils.parms()
		return Output(data={"options":Events().names(),"selected": parms.get("EventID")})

	@app.route("/adm/events/add/",methods=["POST"])
	def adm_events_add() -> dict:
		from db import Events
		parms = Utils.parms()
		try:
			f = parms.get("Image")
			fname = save_file(f)
			del parms["Image"]
			parms["Logo"] = f"/content/{fname}"
			Events().add(data=parms)
		except Exception as e:
			Utils.Debug(title="Trouble saving file",data=e)
		return Output(data={},status=1)
		# return Output(Events().all())

	@app.route("/adm/events/update/",methods=["POST"])
	def adm_events_update() -> dict:
		from db import Events
		from db import Events
		parms = Utils.parms()
		try:
			f = parms.get("Image")
			if f:
				assert f is not None, "No File"
				fname = save_file(f)
				del parms["Image"]
				parms["Logo"] = f"/content/{fname}"

			Events().update(data=parms)
		except Exception as e:
			Utils.Debug(title="Trouble Updating",data=e)
		return Output(data={},status=1)

	@app.route("/adm/events/delete/<event_id>")
	def adm_events_delete(event_id:int) -> dict:
		from db import Events
		return Output(Events().delete(int(event_id)))

	@app.route("/adm/events/clone/<event_id>")
	def adm_events_clone(event_id:int) -> dict:
		from db import Events
		events = Events()
		resp = 0
		event_id = int(event_id)
		try:
			assert event_id > 0, "ID must be number"
			new_event_id = events.clone(id=event_id)
			assert new_event_id>0,f"Trouble Cloning Event {event_id} {new_event_id}"
			return Output(data=new_event_id,status=1,message=f"Success",error=events.error)
		except Exception as e:
			Utils.Debug(title="Trouble Cloning",data=e)
			return Output(message=f"Trouble cloning Event {str(e)}",status=0,data=resp)

	#################################
	## Explorers
	@app.route("/adm/explorers/")
	def adm_explorers() -> dict:
		from db import Explorers
		return Output(Explorers().all())

	@app.route("/adm/explorers/add/")
	def adm_explorers_add() -> dict:
		from db import Explorers
		parms = Utils.parms()
		explorers = Explorers()
		status = explorers.add(data=parms)
		return Output(data={},status=status,error=explorers.error)

	@app.route("/adm/explorers/update/")
	def adm_explorers_update() -> dict:
		from db import Explorers
		parms = Utils.parms()
		explorers = Explorers()
		status = explorers.update(data=parms)
		return Output(data={},status=status,error=explorers.error)

	@app.route("/adm/explorers/delete/<ID>")
	def adm_explorers_delete(ID:int) -> dict:
		from db import Explorers
		parms = Utils.parms()
		explorers = Explorers()
		status = explorers.delete(data={"ID":ID})
		return Output(data={},status=status,error=explorers.error)

	#################################
	## Locations
	@app.route("/adm/locations/")
	def adm_locations() -> dict:
		from db import Locations
		return Output(Locations().all())

	@app.route("/adm/locations/add/",methods=["POST"])
	def adm_locations_add() -> dict:
		from db import Locations
		import os
		import random
		parms = Utils.parms()
		try:
			f = parms.get("Image")
			if f:
				fname = save_file(f)
				del parms["Image"]
				parms["image"] = f"/content/{fname}"
			Locations().add(data=parms)
		except Exception as e:
			Utils.Debug(title="Trouble Updating",data=e)
		return Output(data={},status=1)

	@app.route("/adm/locations/update/",methods=["POST"])
	def adm_locations_update() -> dict:
		from db import Locations
		parms = Utils.parms()
		try:
			f = parms.get("Image")
			if f:
				assert f is not None, "No File"
				fname = save_file(f)
				del parms["Image"]
				parms["image"] = f"/content/{fname}"
			Locations().update(data=parms)
		except Exception as e:
			Utils.Debug(title="Trouble Updating",data=e)
		return Output(data={},status=1)

	@app.route("/adm/locations/delete/<ID>")
	def adm_locations_delete(ID:int) -> dict:
		from db import Locations
		return Output(data={},status=Locations().delete(ID))

	@app.route("/adm/locations/code/<ID>")
	def adm_locations_code(ID:int) -> dict:
		from db import Locations

		return Response(Locations().qr_code(ID),mimetype='image/svg+xml',headers={"Content-Disposition":f"attachment;filename=QRCode_{ID}.svg"})

	@app.route("/test")
	def io_test() -> str:
		parms = Utils.parms()
		return f'ID = {parms.get("ID")}'

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

	def Output(data:Optional[Any],status:int = 1,message:str = "",error:Optional[str]=None) -> dict:
		if error:
			message = error
			status = 0
		return {
			"data":data,
			"status":status,
			"message":message
		}

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

	@app.errorhandler(404)
	def page_not_found(error) -> tuple:
		return 'This page does not exist', 404

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