
from flask import Flask,jsonify,Response,session,request,make_response,g # pyright: ignore
from dataclasses import dataclass,field
import json
from json import dumps,JSONEncoder
from typing import Optional,Any,Self
from datetime import datetime, timedelta,date
import pytz # pyright: ignore
from dateutil import parser # pyright: ignore
from decimal import Decimal
import time
_debug_log = []

def getUserID() -> int:
	return i(session.get("User",{}).get("ID") or -1)

def getUserName() -> str:
	return s(session.get("User",{}).get("Name") or "")

def getIP():
	if request.headers.getlist("X-Forwarded-For"):
		ip = request.headers.getlist("X-Forwarded-For")[0]
	else:
		ip = request.remote_addr
	return ip

class json_encoder(JSONEncoder):
	def default(self, o):
		# import Decimal
		# try:
		# Debug(title="JSON Obj type",data=type(obj),force=True)
		try:
			if isinstance(o, (datetime,date,IODate)):
				return o.isoformat()
			# elif isinstance(obj, Money):
			# 	return str(obj)
			# elif isinstance(obj, Decimal):
			# 	return str(obj)
			elif isinstance(o, timedelta):
				if o.seconds > 0:
					if o.days:
						return "{} days {} seconds".format(o.days,o.seconds)
					else:
						return "{} seconds".format(o.seconds)
				else:
					return int(o.days)
			else:
				try:
					return json.JSONEncoder.default(self, o)
				except:
					try:
						return o.to_JSON()
					except:
						return str(o)
						# if hasattr(obj,'__dict__'):
						# 	return obj.__dict__
						# else:
						# 	print("Unknown Type for json")
						# 	print([type(obj),obj])
						# 	return str(obj)
		except Exception as e:
			print(e)

def is_numeric(val):
	try:
		float(val)
		return(True)
	except:
		return(False)

def toList(ldata:Any) -> list:
	match ldata:
		case None:
			return []
		case list():
			return ldata
		case _:
			return [ldata]

def toFloat(num:Any):
	match num:
		case None:
			return 0.00
		case float():
			return num
		case _:
			try:
				return float(num)
			except Exception as e:
				return float(0)

def toInt(num:Any,default:int = 0):
	match num:
		case None:
			return default
		case int():
			return num
		case _:
			try:
				return int(num)
			except:
				return default

def toStr(item:Any):
	match item:
		case None:
			return ""
		case str():
			return item
		case _:
			return str(item)


def toMoney(num:Any) -> Decimal:
	try:
		match num:
			case None:
				return Decimal("0.00")
			case _:
				return round(Decimal(str(num)),2)
	except Exception as e:
		return Decimal("0.00")

i = toInt
m = toMoney
s = toStr
f = toFloat

def change(before,after):
	try:
		return (float(after) - float(before)) / float(before)
	except:
		return 0

def toJSON(data):
	try:
		if data is None:
			return ""
		else:
			return dumps(data,skipkeys=True,cls=json_encoder)
			# return json_encoder(ensure_ascii=False).encode(data)
	except Exception as e:
		Debug(title="JSON trouble",data=e)
		Debug(data)
		return str(data)

def getJSON(data,d={}):
	if type(data) == type(d):
		return(data)
	
	if not isinstance(data,str):
		return data

	if not data:
		return d

	try:
		return json.loads(data)

	except Exception as e:
		
		try:
			if data.find("'")>=0:
				import ast
				return ast.literal_eval(data) or d
		except:
			pass		

		Debug(title="Trouble Parsing JSON",data=[type(data),data])
		Debug(title="JSON Error",data=e)

		return d

def first(data, default:Any=""):
	try:
		return data[0]
	except:
		return default

def last(data, default:Any=""):
	try:
		return data[-1]
	except:
		return default

def group_by(field:str, data:list,drop_key:bool=False) -> dict:
	rdata = {}
	for row in data:
		key = row.get(field)
		if drop_key:
			del row[field]
		rdata[key] = row
	return rdata

def tst():
	return f"<h2>Woohoo!</h2><p>Debug:</p>"

def url_encode(data:str) -> str:
	import urllib.parse
	return urllib.parse.quote_plus(data)

def url_decode(data:str) -> str:
	import urllib.parse
	return urllib.parse.unquote(data)

def mk_csv(data,fname=None):
	import csv
	import io
	from io import StringIO
	csvfile = io.StringIO()
	val = ""
	try:
		assert len(data)>0, "Empty Data"
		cols = data[0].keys()
		writer = csv.DictWriter(csvfile,fieldnames=cols,dialect='excel-tab')
		writer.writeheader()
		for item in data:
			writer.writerow(item)

		val = csvfile.getvalue()
		if fname:
			print("Writing file {}".format(fname))
			with open(fname) as f:
				f.write(val)

	except Exception as e:
		Debug(title="Trouble",data=e)

	return val

def neg(val) -> int:
	try:
		return -abs(val)
	except:
		return val

def parms() -> dict:
	data = {}

	for k,v in request.form.items():
		data[k]=v

	for k,v in request.args.items():
		data[k]=v
	
	try:
		for f,v in request.files.items():
			data[f] = v
	except Exception as e:
		Debug(title="Trouble with file upload",data=e)

	try:
		for k,v in request.json.items():
			data[k]=v
	except:
		pass
	return data


def div(left,right,rnd=2):
	try:
		result = round(float(left) / float(right),rnd)
		if rnd == 0:
			return int(result)
		else:
			return float(result)
	except:
		return float(0)

def split(s:str,delim:str) -> list:
	try:
		return s.split(delim)
	except:
		return [s]

def errorStr(error) -> str:
	import linecache
	import sys
	try:
		_, exc_obj, tb = sys.exc_info()
		assert tb is not None, "No frame"	
		f = tb.tb_frame
		lineno = tb.tb_lineno
		filename = f.f_code.co_filename
		linecache.checkcache(filename)
		line = linecache.getline(filename, lineno, f.f_globals)
		e_str = 'EXCEPTION IN ({}, LINE {} "{}"): {}'.format(filename, lineno, line.strip(), exc_obj)
	except:
		e_str = str(error)	
	
	return e_str

def Debug(title:str="",data:Any | Exception = None,debug:bool=True):
	# global app
	# global _debug_log,app
	import logging
	logger = logging.getLogger('werkzeug') # grabs underlying WSGI logger
	
	# handler = logging.FileHandler('api.log') # creates handler for the log file
	# logger.addHandler(handler) # adds handler to the werkzeug WSGI logger
	# app.logger.info(data)
	data = data or ""
	if not debug:
		return
	d = ""
	match data:
		case Exception():
			# import linecache
			import traceback

			import sys
			_, _, tb = sys.exc_info()
			tbinfo = traceback.extract_tb(tb)[-1]
			# lineno = tbinfo.lineno
			# filename = tbinfo.filename
			# line = tbinfo.line

			# f = tb.tb_frame
			# lineno = tb.tb_lineno
			# filename = f.f_code.co_filename
			# linecache.checkcache(filename)
			# line = linecache.getline(filename, lineno, f.f_globals)
			# d = data
			# logger.info('EXCEPTION IN ({}, LINE {} "{}"): {}'.format(filename, lineno, line.strip(), exc_obj))
			#d = 'EXCEPTION IN ({}, LINE {} "{}"): {}'.format(filename, lineno, line.strip(), exc_obj)
			d = traceback.format_exc();
		case dict() | list():
			d = dumps(data,indent=5,ensure_ascii=True,cls=json_encoder)
		case str() | int() | tuple():
			d = data
		case None:
			pass
		case _:
			d = str(data)
	if not d:
		d = str(data)

	print(f"*************************{title}***********************")
	print(d)
	print(f"***************{type(data)}**********************")
	
	logger.info(f"*************************{title}***********************")
	logger.info(d)
	logger.info(f"***************{type(data)}**********************")


@dataclass
class Output():
	data: dict[str,str] = field(default_factory=dict)
	status: int = 1
	message: str = ""

	def __str__(self):
		return self.json
	
	@property
	def obj(self):
		return {"data":self.data,"status":self.status,"message":self.message}

	@property
	def json(self):
		return jsonify(self.obj)

	@property
	def response(self):
		return self.json

class IODate(object):
	quarterDates = {1:("1-1","3-31"),2:("4-1","6-30"),3:("7-1","9-30"),4:("10-1","12-31")}
	dayNameInts = {"Monday":0,"Tuesday":1,"Wednesday":2,"Thursday":3,"Friday":4,"Saturday":5,"Sunday":6}
	monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"]
	shortMonthNames = ["Jan","Feb","Mar","Apr","May","Jun","Ju","Aug","Sep","Oct","Nov","Dec"]
	daylightDelta = 8
	useDayLight = False
	date:datetime
	def __init__(self,dStr=None,tzone=None):
		self.set(dStr)
		self.utc().pst()
		if tzone:
			self.tz(tzone)

	def _(self):
		return self.dateStr()

	@property
	def ym(self) -> str:
		return self.dateStr()[0:7]

	@property
	def age(self) -> int:
		return self.daysFromNow(abs=False)

	@property 
	def work_week(self):
		first = self.clone().delta(days=-(self.dayOfWeek()-1))
		last = first.clone().delta(days=6)
		return first,last
	
	@property
	def work_week_id(self):
		first,last = self.work_week
		return "{}::{}".format(first,last)

	@property
	def week(self):
		try:
			assert self.date is not None, "No date set"
			return self.date.isocalendar()[1]
		except:
			return -1

	@classmethod
	def list_range(cls,start,end):
		startDate = IODate(start)
		endDate = IODate(end)
		days = []
		while startDate.timestamp <= endDate.timestamp:
			days.append(startDate.dateStr())
			startDate.delta(days=1)
		return days

	@classmethod
	def fromTimestamp(cls,tstamp):
		return IODate(datetime.fromtimestamp((f(tstamp)/1000)))

	@classmethod
	def unStamp(cls,seconds):
		hours = int(seconds/3600)
		minutes = int((seconds%3600)/60)
		return {"hours":hours,"minutes":minutes}

	@classmethod
	def unStampStr(cls,seconds):
		t = cls.unStamp(seconds)
		return "{}h {}m".format(t["hours"],t["minutes"])

	@classmethod
	def withtimestamp(cls,timestamp):
		d = datetime.fromtimestamp(timestamp)
		return cls(d)

	@classmethod
	def optional(cls,dateStr):
		if dateStr is not None:
			return IODate(dateStr)
		else:
			return None

	@classmethod
	def monthName(cls,num,short=False):
		num = int(num)
		if num> 0 and num < len(cls.monthNames):
			if short:
				return cls.shortMonthNames[num-1]
			else:
				return cls.monthNames[num-1]

	@property
	def quarter(self):
		return (self.month-1)//3 + 1

	@classmethod
	def listMonthRange(cls,start=-30,end=0):
		endDate = IODate(end)
		cur = IODate(start)
		done = False
		months = []
		while not done:
			q_str = "{}-{}".format(cur.year,cur.month)

			if (not q_str in months) and int(cur)<=int(endDate):
				months.append(q_str)

			if int(cur)>int(endDate):
				done = True
			cur.delta(days=1)
		return months

	@classmethod
	def listQuarterRange(cls,start=-30,end=0):
		# startDate = IODate(start)
		endDate = IODate(end)
		cur = IODate(start)
		done = False
		quarters = []
		while not done:
			quarter = cur.quarter
			q_str = "{}-{}".format(cur.year,quarter)

			if (not q_str in quarters) and int(cur)<=int(endDate):
				quarters.append(q_str)

			if int(cur)>int(endDate):
				done = True
			cur.delta(days=1)
		return quarters

	@classmethod
	def year_range(cls,start,end):
		start = IODate(start)
		end = IODate(end)
		years = []
		for i in range(abs(start.year-end.year)+1):
			years.append(int(start.year)+int(i))
		return years


	def set(self,dStr):
		if isinstance(dStr, IODate):
			self.date = dStr.date
		elif isinstance(dStr,datetime):
			self.date = dStr
		elif is_numeric(dStr):
			self.date = datetime.now()
			self.delta(int(dStr))
		elif dStr:
			try:
				self.date = parser.parse(str(dStr))
			except Exception as e:
				Debug(title=dStr,data=e)
				self.date = datetime.now()
				# self.date = datetime(1977, 11, 9)
				self.pst()
		else:
			self.date = datetime.now()

	def setDate(self,*argc,**argv):
		try:
			assert self.date is not None, "No Date"
			self.date = self.date.replace(*argc,**argv)
		except:
			pass
		return self

	def setTime(self,hour=0,minute=0,second=0):
		self.time(hour=hour,minute=minute,second=second)
		return(self)

	def start(self,setDate=False):
		self.time(hour=0,minute=0,second=0)
		return(self)

	def end(self,setDate=False):
		self.time(hour=23,minute=59,second=59)
		return self

	def first_date(self,time=False):
		if not self.date:
			return self
		self.date = self.date.replace(day=1)
		if time:
			self.start()
		return self

	def last_date(self,time=False):
		try:
			assert self.date is not None, "No Date"
			month_days = self.daysInMonth()
			self.date = self.date.replace(day=month_days)
			if time:
				self.end()
		except:
			pass
		return self

	def sqlformat(self):
		try:
			assert self.date is not None, "No Date"
			return(self.date.strftime("%Y-%m-%d %H:%M:%S"))
		except:
			return ""
		
	def sql(self):
		return(self.sqlformat())

	def __repr__(self):
		return str(self)
	
	def is_between(self,start,end):
		start = IODate(start)
		end = IODate(end)
		return (self.timestamp >=start.timestamp and self.timestamp<=end.timestamp)

	def quarterDate(self,quarter=None,year=None):
		quarter = toInt(quarter,self.quarter)
		try:
			assert self.date is not None, "No Date"
			year = year if year else self.date.year
			Debug(data = "Quarter: {}".format(quarter),title = "Quarter")

			if quarter > 4 or quarter < 1:
				Debug(title="quarterDate: bad quarter",data={"quarter":quarter})
				quarter = self.quarter

			start = IODate(datetime.strptime("{} {}".format(year,self.quarterDates[quarter][0]),"%Y %m-%d"))
			end = IODate(datetime.strptime("{} {}".format(year,self.quarterDates[quarter][1]),"%Y %m-%d"))
			return (start,end)
		except:
			# Debug(data = "{}=>{}".format(start.dayStr(),end.dateStr()),title = "Start End Dates")
			return(0,0)

	def getQuarterDates(self,quarter=None):
		q = {1:(1,90),2:(91,180),3:(181,270),4:(271,365)}

		curQ = quarter if quarter else self.quarter
		dates = []
		try:
			assert self.date is not None, "No Date"
			year = toInt(self.date.strftime("%Y"))

			for _ in range(4):
				curQ = curQ - 1
				if curQ == 0:
					year = year - 1
					curQ = 4

				days = q[curQ][0]
				Debug(title="getQuarterDates=>days",data={"days":days})
				dates.append((
						datetime.strptime("{} {}".format(year,self.quarterDates[curQ][0]),"%Y %m-%d"),
						datetime.strptime("{} {}".format(year,self.quarterDates[curQ][1]),"%Y %m-%d")
					))
		except:
			pass
		return dates


	def time(self,hour=0,minute=0,second=0)->Self:
		try:
			assert self.date is not None, "No Date"
			self.date = self.date.replace(hour=hour,minute=minute,second=second)
		except:
			pass
		return self

	def clone(self):
		import copy
		return copy.deepcopy(self)

	def dayOfWeek(self) -> int:
		try:
			assert self.date is not None, "No date"
			return int(self.date.strftime("%u"))
		except:
			return -1

	def dayOfWeekStr(self) -> str:
		try:
			assert self.date is not None, "No date"
			return self.date.strftime("%a")
		except:
			return ""

	def daysInMonth(self):
		import calendar
		try:
			assert self.date is not None, "No Date"
			return calendar.monthrange(self.date.year, self.date.month)[1]
		except:
			return -1


	def week_of_year(self)->int:
		try:
			assert self.date is not None, "No Date"
			return int(self.date.isocalendar()[1])

		except:
			return -1

	def datetime(self):
		return str(self.date)

	def display_date(self)->str:
		try:
			assert self.date is not None, "No Date"
			return self.date.strftime("%b %e %Y")

		except:
			return ""
	
	def short_date(self,year=True):
		try:
			assert self.date is not None, "No Date"
			if year:
				return self.date.strftime("%y-%m-%d")
			else:
				return self.date.strftime("%m-%d")
		except:
			return ""

	def dayStr(self,year=True) ->str:
		try:
			assert self.date is not None, "No Date"
			if year:
				return self.date.strftime("%Y-%m-%d")
			else:
				return self.date.strftime("%m-%d")
		except:
			return ""

	def travel(self,days=0,hours = 0, seconds=0,tdelta=None)->Self:
		try:	
			assert self.date is not None, "No Date"
			self.date = self.date + timedelta(days = days,hours = hours,seconds=seconds)
		except:
			pass
		return self

	def delta(self,days=0,hours = 0, seconds=0,minutes=0,tdelta=None,months=0):
		if minutes:
			seconds += minutes*60

		self.date = self.date + timedelta(days = days,hours = hours,seconds=seconds)
		if months>0:
			from dateutil.relativedelta import relativedelta #pyright ignore
			self.date = self.date.today() + relativedelta(months=months)
		return self

	def deltaDays(self,dateVal):
		try:
			assert self.date is not None, "No Date"
		except:
			return dateVal	

		if type(dateVal) is IODate and dateVal.date is not None:

			diff = (dateVal.date - self.date).days
		elif type(dateVal) is datetime:
			diff = (dateVal - self.date).days
		else:
			v = IODate(dateVal)

			diff = (v.date  - self.date).days
		return diff

	def __add__(self,val):
		try:
			assert self.date is not None, "No Date"
		except:
			return val	

		if isinstance(val, IODate) and val and val.date is not None:
			return int(self) + int(val)
		elif str(val).find("-")>=1 or str(val).find("/") >=0:
			return int(self) + int(IODate(val))
		else:
			return IODate(self.date + timedelta(days=int(val)))

	def __sub__(self,val):
		try:
			assert self.date is not None, "No Date"
		except:
			return val	
		
		if isinstance(val, IODate) and val.date is not None:
			return val.date - self.date
		elif str(val).find("-") >=0 or str(val).find("/") >=0:
			try:
				v = IODate(val)
				assert v.date is not None, "No val.date"
				return v.date - self.date
			except:
				return self.date
		else:
			return IODate(self.date - timedelta(days=int(val)))

	def __iadd__(self,val):
		try:
			assert self.date is not None, "No Date"
		except:
			return val	

		if isinstance(val, timedelta):
			self.date = self.date + val
			return self.date
		else:
			self.date = self.date + timedelta(days=val)
			return self.date

	def __isub__(self,val):
		try:
			assert self.date is not None, "No Date"
		except:
			return self.date	

		if isinstance(val, timedelta):
			self.date = self.date - val
		else:
			self.date = self.date - timedelta(days=val)
		return self

	def __eq__(self,val):
		return self.__int__() == int(val)

	def add(self,val):
		return self.__add__(val)

	def sub(self,val):
		return self.__sub__(val)

	def iadd(self,val):
		return self.__iadd__(val)

	def isub(self,val):
		return self.__isub__(val)

	def to_JSON(self):
		return self.__str__()

	def daysFromNow(self,abs=True):
		today = IODate().pst()
		cdate = self.clone().pst()

		days = (today.date - cdate.date).days
		if abs or int(today) < int(cdate):
			return days
		else:
			return -days

	def today(self):
		return self.dateStr()

	def dateStr(self,year=True):
		if year:
			return str(self.date.strftime("%Y-%m-%d"))
		else:
			return str(self.date.strftime("%m/%d"))

	def datetimeStr(self):
		return str(self.full())

	@classmethod
	def Now(cls):
		return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

	def now(self):
		return self.full()

	def full(self):
		return str(self.date.strftime("%Y-%m-%d %H:%M:%S"))

	def timeStr(self,showSeconds=False,showDelim=True):
		if showSeconds:
			s = "%H:%M:%S"
		else:
			s = "%l:%M"

		if showDelim:
			s+=" %p"

		return self.date.strftime(s)

	def str(self,full=False):		
		if full:
			return self.full()

		return str(self)

	def amz_str(self):
		self.pst().utc()
		return(self.date.strftime("%Y-%m-%dT%H:%M:%SZ"))

	def amz_adjust(self):
		self.pst().utc()
		return self

	def amz_adjust_start(self):
		self.pst().start().utc()
		return self

	def amz_adjust_end(self):
		self.pst().end().utc()
		return self

	def to_local_time(self,mode=None):
		self.utc()
		if mode=='start':
			self.start()
		elif mode =='end':
			self.end()
		return self.pst()

	def to_amz_time(self,mode=None):
		self.pst()
		if mode=='start':
			self.start()
		elif mode =='end':
			self.end()
		return self.utc()

	def str_delta(self,dateStr):
		import parsedatetime as pdt # pyright: ignore
		from time import mktime
		cal = pdt.Calendar()
		time_struct, _ = cal.parse(dateStr,self.date)
		self.date = datetime.fromtimestamp(mktime(time_struct))
		return self

	def __getattr__(self, name):
		return getattr(self.date, name)

	@property
	def timestamp(self):
		return self.__int__()

	def tstamp(self) -> float:
		try:
			assert self.date is not None, "No Date"
			return (self.date - datetime(1970, 1, 1)).total_seconds()
		except:
			return -1

	def __int__(self) ->int:
		try:
			assert self.date is not None, "No Date"
			if self.tzinfo:
				# print("Converting TZ")
				TZ = pytz.timezone('UTC')
				epoch = TZ.localize(datetime(1970, 1, 1))
				return int((self.date - epoch).total_seconds())
			else:
				# print("No TZ")
				return int((self.date - datetime(1970, 1, 1)).total_seconds())
		except:
			return -0

	def __float__(self) ->float:
		try:
			assert self.date is not None, "No Date"
			if self.tzinfo:
				TZ = pytz.timezone('UTC')
				epoch = TZ.localize(datetime(1970, 1, 1))
				return float((self.date - epoch).total_seconds())
			else:
				return float((self.date - datetime(1970, 1, 1)).total_seconds())
		except:
			return -0.00
	# def __lt__(self,other):
	# 	return float(self) < float(other)

	# def __le__(self,other):
	# 	return float(self) <= float(other)

	# def __eq__(self,other):
	# 	return float(self) == float(other)

	# def __ne__(self,other):
	# 	return not self.__eq__(other)

	# def __gt__(self,other):
	# 	return float(self) > float(other)

	# def __ge__(self,other):
	# 	return float(self) >= float(other)

	def __str__(self)->str:
		try:
			assert self.date is not None, "No Date"
			return str(self.date.strftime("%Y-%m-%d"))
		except:
			return ""

	def week_day_name(self):
		try:
			assert self.date is not None, "No Date"
			return self.date.strftime("%a")
		except:
			return ""
	def tz(self,zone):
		TZ = pytz.timezone(zone)
		try:
			assert self.date is not None, "No Date"
			self.date = TZ.localize(self.date)
		except:
			self.date = self.date.astimezone(TZ) #pyright: ignore
		return self

	def pst(self):
		try:
			self.tz('US/Pacific')
		except Exception as e:
			pass
		return self

	def utc(self):
		try:
			self.tz('UTC')
		except:
			pass
		return self

	def dst_delta(self):
		return timedelta(hours=IODate.daylightDelta)

	def amazon_time(self):
		try:
			assert self.date is not None, "No Date"
			return self.date - self.dst_delta()
		except:
			return self.date

	def iso_ext(self):
		try:
			assert self.date is not None, "No Date"
			return self.date.strftime("%Y-%m-%dT%H:%M:%S.%f0%z")
		except:
			return ""

	def amz(self,full=True):
		try:
			assert self.date is not None, "No Date"
			if full:
				return self.date.isoformat()
			else:
				return self.date.strftime("%Y-%m-%dT%k:%M:%S")
		except:
			return ""


def exception_info(e) -> str:
	import linecache
	import sys
	tb = None
	try:
		exc_type, exc_obj, tb = sys.exc_info() 
		assert tb is not None, "No TB"
		f = tb.tb_frame
		lineno = tb.tb_lineno
		filename = f.f_code.co_filename
		linecache.checkcache(filename)
		line = linecache.getline(filename, lineno, f.f_globals)
		return(f'EXCEPTION IN ({filename}, LINE {lineno} "{line.strip()}"): {exc_obj}')
	except Exception as e:
		return(f'Trouble getting Debug: {e}::>>{tb}')

def b64_encode(input):
	import base64
	dataBase64 = base64.b64encode(input)
	dataBase64P = dataBase64.decode("UTF-8")
	# Debug(title="base64",data=[dataBase64,dataBase64P])
	return dataBase64P

def b64_decode(input):
	import base64
	return base64.decodebytes(input.encode("ascii"))

def encr(data:dict) -> str:
	from Crypto.Cipher import AES #pyright: ignore
	# from Crypto import Random	#pyright: ignore
	secret = b"7313377313377311"
	j = dumps(data)
	
	s = bytes(j,"UTF-8")
	try:
		if not data:
			return ""
		# data = data.encode('utf-8')
		# iv = Random.new().read(AES.block_size)
		iv = b'\xcf\xbc\x13)"X\xf0\x18]\x9f6n\xe1\xca\xe4\x83'
		cipher = AES.new(secret, AES.MODE_CFB, iv)
		msg = iv + cipher.encrypt(s)
		return b64_encode(msg)
		# return hexlify(msg).decode("utf-8")
	except Exception as e:
		print(e)
		Debug(title="Encrypt",data={"data":data,"Error":e})
		return ""

def decr(data:str) -> Any:
	from Crypto.Cipher import AES #pyright: ignore
	# from Crypto import Random  #pyright: ignore

	secret = b"7313377313377311"

	d = b64_decode(data)
	try:
		if not data:
			return ""
		# iv = Random.new().read(AES.block_size)
		iv = b'\xcf\xbc\x13)"X\xf0\x18]\x9f6n\xe1\xca\xe4\x83'
		cipher = AES.new(secret, AES.MODE_CFB, iv)
		# msg = cipher.decrypt(codecs.decode(data,"hex"))[len(iv):]
		msg = cipher.decrypt(d)[len(iv):].decode("UTF-8")
		return msg
	except Exception as e:
		print(e)
		Debug(title="Decrypt",data={"data":data,"Error":e})
		return ""

def isset(var:Any) ->bool:
	try:
		if isinstance(var,str):
			return var in globals()
		else:
			return (var is not None)
	except:
		return(False)

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'


START_TIME = 0

def print_request():
	dt = IODate()
	global START_TIME
	global app
	try:
		assert not isset(START_TIME), "No timer START_TIME"
		assert START_TIME is not None, "No timer START_TIME"
		assert isset("app"), "No app for logger"
		END_TIME = time.perf_counter()
		ms = (END_TIME - START_TIME) * 1000
		app.logger.warning(f"[IO] {dt.full()} | {bcolors.OKBLUE} {ms:.4f}ms {bcolors.ENDC}| {bcolors.OKGREEN} {request.path} {bcolors.ENDC} |")	
	except Exception as e:
		Debug(title="Print Request Trouble",data=e)
