from flask import Flask, jsonify, render_template, url_for, request, redirect, json, make_response
from flask_sqlalchemy import SQLAlchemy, functools, Model
from flask_cors import CORS, cross_origin
from datetime import datetime, timedelta
from functools import wraps
from flask_sqlalchemy_cache import CachingQuery, FromCache
from flask_cache import Cache
from apscheduler.schedulers.background import BackgroundScheduler


import pymysql, os, math, requests, uuid, sys

# pymysql.install_as_MySQLdb()

# from routes.salesforce_v2 import send_visit_data_scheduled

app = Flask(__name__)
CORS(app)

# app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://root@localhost/bookings_db"
# app.config['SENDGRID_API_KEY'] = "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"
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['SQLALCHEMY_DATABASE_URI']
app.config['SENDGRID_API_KEY'] = os.environ['SENDGRID_API_KEY']
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_MAX_OVERFLOW'] = 5
app.config['SQLALCHEMY_POOL_SIZE'] = 40
if os.environ['ENVIRONMENT'] == "Development":
	app.config['SERVER'] = os.environ['DEVELOPMENT_IP']
	app.config['SERVER_IP'] = os.environ['DEVELOPMENT_IP']
elif os.environ['ENVIRONMENT'] == "Production":
	app.config['SERVER'] = os.environ['PRODUCTION_IP']
	app.config['SERVER_IP'] = os.environ['SERVER_IP']
app.config['MEMBERSHIP_SERVICE'] = 'https://{}:5004/v1/members/reports'.format(
	app.config['SERVER'])
app.config[
	'VOLUNTEER_SERVICE'] = 'https://{}:5012/v1/applications/reports'.format(
		app.config['SERVER'])
app.config['DONATION_SERVICE'] = 'https://{}:5008/v1/donations/reports'.format(
	app.config['SERVER'])
app.config[
	"LOGGING_URL"] = "http://bookings.olpejetaconservancy.org:900/request/details/new"
app.config[
	"LOG_AUTH_KEY"] = "UsIUkFDNUL4wm2AGbZoPoNzXGHgzlXKcJFk9M3J2iP_jOB30dtVK24NjOjdN_d5052lUg_udIoPhftpsHtOYqQ"
app.config["BOOKING_KEY"] = "826229ff88b14e5f8387f7f489f64759"
app.config["JSONIFY_PRETTYPRINT_REGULAR"] = False
app.config["JSON_SORT_KEYS"] = False

## Status colour config values
app.config["CONFIRMED"] = "#0084ff"
app.config["UNCONFIRMED"] = "#f0932b"
app.config["ABANDONED"] = "#00a8ff"
app.config["CANCELLED"] = "#e74c3c"
app.config["NO_SHOW"] = "#9b59b6"
app.config["UPDATED"] = "#00cec9"
app.config["TO_INVOICE"] = "#9b59b6"
app.config["DEPOSIT"] = "#487eb0"
app.config["COMPLIMENTARY"] = "#8c7ae6"
app.config["CHECKED_IN"] = "#2196f3"
app.config["CHECKED_OUT"] = "#73a533"
app.config["POSTPONED"] = "#800080"

app.config["SF_CONSUMER_SECRET"] = "8352622747345177671"
app.config[
	"SF_CONSUMER_KEY"] = "3MVG9yZ.WNe6byQD858v6Un58ByCtjUKlb8DId1hjyRhGxOXwS2PGaE.vG46BdI_.uX.whSJwkx8clIJr1ogh"
app.config[
	"SF_REDIRECT_URI"] = "https://bookings.olpejetaconservancy.org:5005/callback"

Model.query_class = CachingQuery
db = SQLAlchemy(app, session_options={'query_cls': CachingQuery})

db_cache = Cache(app)

memoise_cache = Cache(app,
					  config={
						  "CACHE_TYPE": "filesystem",
						  "CACHE_DIR": "/var/backups"
					  })

report_cache = Cache(
	app,
	config={
		"CACHE_TYPE": "filesystem",
		"CACHE_DIR": "/var/report_cache",
		"CACHE_DEFAULT_TIMEOUT":
		0  # Timeout of 0 indicates that the cache never expires
	})

schoolCodeInterval = 1
schoolZeroPadding = 6

ticketCodeInterval = 1
ticketZeroPadding = 6

from database.session import SessionTracking

from functions.logging_service import Logger

bookings_logger = Logger(app.config["BOOKING_KEY"])

receipt_print_options = {
	'page-width': '10.0in',
	'page-height': '50.0in',
	'minimum-font-size': '50',
	'margin-top': '0.0in',
	'margin-right': '0.0in',
	'margin-bottom': '0.0in',
	'margin-left': '0.0in',
	'dpi': '400',
	'encoding': "UTF-8",
	'custom-header': [('Accept-Encoding', 'gzip')],
	'zoom': '3.4'
}


def invoice_options(current_date):
	invoice_print_options = {
		'encoding':
		"UTF-8",
		'custom-header': [('Accept-Encoding', 'gzip')],
		'zoom':
		'0.75',
		'footer-left':
		'Invoice generated at ' + current_date.strftime("%I:%M %p") + ' on ' +
		current_date.strftime("%d %B %Y"),
		'footer-font-size':
		6
	}

	return invoice_print_options


from routes.salesforce_v2 import send_visit_data, send_driver_details, send_visit_data_scheduled


# @app.route("/sf/post")
def salesforce_record_keeping():
	send_visit_data_scheduled()
	# send_driver_details()


## https://apscheduler.readthedocs.io/en/latest/modules/triggers/cron.html
## https://apscheduler.readthedocs.io/en/latest/
## https://stackoverflow.com/questions/21214270/scheduling-a-function-to-run-every-hour-on-flask
schedule_task = BackgroundScheduler(daemon=True)
schedule_task.add_job(salesforce_record_keeping,
					  "cron",
					  day_of_week="*",
					  hour="2",
					  minute="30")
schedule_task.start()


def sessionTracking(func):
	@wraps(func)
	def getHeaderDetails(*args, **kwargs):
		# Get header details
		get_header_details = request.headers

		header_dict = dict(get_header_details)

		session = SessionTracking(method=request.method,
								  url=request.url,
								  headers=str(header_dict),
								  user_agent=header_dict["User-Agent"],
								  language=header_dict["Accept-Language"],
								  platform=request.user_agent.platform,
								  browser=request.user_agent.browser,
								  created_at=datetime.now())

		db.session.add(session)

		db.session.commit()
		db.session.close()

		# Return the requested URL
		return func(*args, **kwargs)

	return getHeaderDetails


def schoolCodeGenerator(prevCode):
	if not prevCode:
		code_prefix = "S"
		prev_code_value = "0"

	else:
		code_prefix, prev_code_value = prevCode.split("-")

	new_code_value = int(prev_code_value) + schoolCodeInterval

	school_code = str(new_code_value).zfill(schoolZeroPadding)

	return code_prefix + "-" + school_code


def ticketGenerator(prevCode):
	if not prevCode:
		code_prefix = "TKT" + datetime.now().strftime("%d%m%Y")
		prev_code_value = "0"

	else:
		code_prefix, prev_code_value = prevCode.split("-")

	new_code_value = int(prev_code_value) + ticketCodeInterval

	ticket_code = str(new_code_value).zfill(ticketZeroPadding)

	return code_prefix + "-" + ticket_code


## The next two functions are called in the invoice.html template in order to correctly format prices
## Reference: https://stackoverflow.com/questions/6036082/call-a-python-function-from-jinja2
@app.context_processor
def format_fee():
	def return_formatted_fee(amount):
		amount = math.ceil(round(amount, 2))

		try:
			split_amount = format(amount).split(".")
			significant = int(split[0])
			decimal = 0
		except Exception:
			significant = int(amount)
			decimal = 0

		if decimal == 0:
			formatted = "{:,}".format(significant)

		return formatted

	return dict(return_formatted_fee=return_formatted_fee)

def end_of_day_evening():
    endOfDayEvening()

    # send_driver_details()
def end_of_day_morning():
    endOfDayMorning()
@app.context_processor
def format_tax():
	def return_formatted_tax(amount):
		## TODO: Confirm display
		# amount = math.ceil(round(amount, 2))

		try:
			split_amount = str(amount).split(".")
			significant = int(split_amount[0])
			decimal = int(split_amount[1])
		except Exception:
			significant = int(amount)
			decimal = 0

		if decimal > 0:
			formatted_decimal = str(split_amount[1])
		elif decimal == 0:
			formatted_decimal = "00"

		formatted_significant = "{:,}".format(significant)

		formatted = formatted_significant + "." + formatted_decimal

		return formatted

	return dict(return_formatted_tax=return_formatted_tax)


from routes.bookings_operations_urls import changeToNoShows,endOfDayEvening,endOfDayMorning#, highestSale

def no_shows():
	changeToNoShows()
	# send_driver_details()

# def highest_sale():
# 	highestSale();

## https://apscheduler.readthedocs.io/en/latest/modules/triggers/cron.html
## https://apscheduler.readthedocs.io/en/latest/
## https://stackoverflow.com/questions/21214270/scheduling-a-function-to-run-every-hour-on-flask
schedule_task_show = BackgroundScheduler(daemon=True)
schedule_task_show.add_job(no_shows,
						   "cron",
						   day_of_week="*",
						   hour="8",
						   minute="30")
schedule_task_show.start()
schedule_task = BackgroundScheduler(daemon=True)
schedule_task_show.add_job(end_of_day_evening,
                           "cron",
                           day_of_week="*",
                           hour="16",
                           minute="20")
schedule_task.start()

schedule_task = BackgroundScheduler(daemon=True)
schedule_task_show.add_job(end_of_day_morning,
                           "cron",
                           day_of_week="*",
                           hour="8",
                           minute="00")
schedule_task.start()

# schedule_task = BackgroundScheduler(daemon=True)
# schedule_task_show.add_job(highest_sale,
#                            "cron",
#                            day_of_week="*",
#                            hour="11",
#                            minute="11")
# schedule_task.start()

from database.bookings import Booking
from variables import get_currency_rate_at_time


@app.context_processor
def convert_amount():
	def return_converted_amount(currency_to, currency_from, amount,
								buying_rate, selling_rate, booking_ref):
		amount = round(amount, 4)

		if currency_to == currency_from:
			return float(amount)
		elif currency_to != currency_from:
			if currency_from == "162fface-f5f1-41de-913b-d2bb784dda3a":
				get_booking = db.session.query(Booking)\
					  .filter(Booking.deletion_marker == None)\
					  .filter(Booking.booking_ref_code == booking_ref)\
					  .first()

				booking_date = get_booking.created_at.strftime("%Y-%m-%d")
				post_data = {"currency_id": currency_to, "date": booking_date}

				get_rates = requests.post(get_currency_rate_at_time,
										  json=post_data)

				buying_rate = get_rates.json()["data"][0]["buy_amount"]

				value = float(amount) / float(buying_rate)
				return float(value)

			elif currency_to == "162fface-f5f1-41de-913b-d2bb784dda3a":
				value = float(amount) * float(selling_rate)
				return float(value)

	return dict(return_converted_amount=return_converted_amount)


## delete this comment
from routes import seed_functions

from routes import base_urls, payment_methods_urls, partners_payment_methods_urls, guests_payment_methods_urls, booking_types_urls
from routes import bookings_urls, bookings_facility_urls, bookings_inventory_urls, bookings_search_urls, calendar_urls, old_calendar_urls
from routes import express_checkout_urls, bookings_payments_urls, vehicles_urls, bookings_batchfile_urls
from routes import bookings_count_urls, payments_urls, destinations_urls, partner_bookings_urls, group_bookings_urls, reminders_urls
from routes import gate_urls, bookings_graphs_urls, print_urls, pickup_locations_urls
from routes import bookings_csv_urls, booking_filters, facility_pricing_types_urls

from routes import mandatory_payments_urls, gatepass_urls, batchfile, salesforce_v2, bookings_operations_urls
from routes import contact_list_urls, callback_urls, booking_notes_urls, documents_urls, payment_gateways_urls
from routes import booking_reports_urls, booking_activity_urls, school_urls, credit_note_urls,entry_mode_urls,booking_finances