# coding=utf-8

"""
This module, views.py, is where all the backend and frontend server requests are handled and returned to the user.
"""

# Needed to send back a rendered HTML page.
from django.shortcuts import render
# Needed for sending a simple HttpResponse such as a string response.
from django.http import HttpResponse
# Needed for allowing POST requests without requiring a CSRF token.
from django.views.decorators.csrf import csrf_exempt
# Needed for user verification.
from django.contrib.auth import authenticate, login, logout
# Needed for requiring the user to be logged in.
from django.contrib.auth.decorators import login_required
# Needed to perform HttpRequests to run Locust.
import requests
# Needed for making JsonResponses.
from django.http import JsonResponse
# Needed to check data on Users.
from django.contrib.auth.models import User

import sys
#sys.path.remove('/home/dev_usr/urbtek/nexus_django')
sys.path.append('/home/dev_usr/urbtek')
# Needed for database access.
from database import database_api
from database import table_definitions
# Needed for sending emails.
from universal_code import sms_sender
# Needed for debugging.
from universal_code import debugging as dbg

from quality_assurance.load_testing import locust_api

import datetime
import time

import pytz

# Importing custom defined models.
from .models import ScheduledTextNotification
from .models import Dividend
from .models import Project
from .models import SeleniumTest
from .models import TestSuiteRuns
from .models import SpecificSeleniumTestRun
from .models import WorkHours
from .models import Feedback

from universal_code import nexus_build_number as nbm
from quality_assurance.selenium_scripts.qa import qa_selenium_scripts as qss
from quality_assurance.selenium_scripts import test_parser
from quality_assurance.selenium_scripts import selenium_base

import ast

'''  __        __   __                    ___          ___                   __          __        ___  __         ___            __  ___    __        __
	/ _` |    /  \ |__)  /\  |       |  |  |  | |    |  |  \ /    \  /  /\  |__) |  /\  |__) |    |__  /__`  _|_  |__  |  | |\ | /  `  |  | /  \ |\ | /__`
	\__> |___ \__/ |__) /~~\ |___    \__/  |  | |___ |  |   |      \/  /~~\ |  \ | /~~\ |__) |___ |___ .__/   |   |    \__/ | \| \__,  |  | \__/ | \| .__/
'''

# If the peon is currently free or not.
peon_is_free = False

# Get the number of git commits made to this project.
NEXUS_BUILD_NUMBER = nbm.get_nexus_build_number()

# Needed for database operations.
db          = database_api.DatabaseConnectionForNexus()
db_api      = database_api.DatabaseAPI(db)

# Needed for getting information on and running selenium tests.
qa_selenium_scripts = qss.QASeleniumScripts()

UTF_8 = 'utf-8'
current_locust = None

LOGIN_URL = 'login'

NEXUS_FRONT_END               = 'templates/nexus_front_end/'
OUTSIDE_PAGES                 = NEXUS_FRONT_END + 'outside_pages/'

OUTSIDE_PAGES                 = NEXUS_FRONT_END + 'outside_pages/'
OUTSIDE_PAGES_LOGIN           = OUTSIDE_PAGES   + 'login.html'
OUTSIDE_PAGES_REGISTER        = OUTSIDE_PAGES   + 'register.html'
OUTSIDE_PAGES_ACCOUNT_RECOVER = OUTSIDE_PAGES   + 'account_recover.html'

PERSONAL_PAGES                    = NEXUS_FRONT_END + 'personal'
PERSONAL_PAGES_TRADING            = PERSONAL_PAGES  + '/trading.html'
PERSONAL_PAGES_HOME               = PERSONAL_PAGES  + '/home.html'
PERSONAL_PAGES_PROFILE            = PERSONAL_PAGES  + '/profile.html'
PERSONAL_PAGES_SLEEP              = PERSONAL_PAGES  + '/sleep.html'
PERSONAL_PAGES_TEXT_NOTIFICATIONS = PERSONAL_PAGES  + '/text_notifications.html'

QA_PAGES                      = NEXUS_FRONT_END + 'qa'
QA_PAGES_NATIONALS            = QA_PAGES + '/nationals/nationals.html'
QA_PAGES_SELF                 = QA_PAGES + '/self_testing/qa_self_testing.html'
QA_PAGES_SELF_SELENIUM_TESTS  = QA_PAGES + '/self_testing/qa_selenium_tests.html'
QA_PAGES_SK                   = QA_PAGES + '/sk/sk_selenium_tests.html'

ADMIN_PAGES             = NEXUS_FRONT_END + 'admin/'
ADMIN_PAGES_ADMIN       = ADMIN_PAGES + 'admin.html'
ADMIN_PAGES_DATABASE    = ADMIN_PAGES + 'database.html'
ADMIN_PAGES_SERVERS     = ADMIN_PAGES + 'servers.html'

SCC_PAGES                         = NEXUS_FRONT_END + 'scc/'
SCC_PAGES_IMAGE_COMPRESSION       = SCC_PAGES + 'image_compression.html'
PERSONAL_PAGES_WORK_HOURS_LOGGING = SCC_PAGES + 'work_hours_logging.html'

PEON_URL    = 'peon_url'
PEON_IP_URL = 'http://23.253.22.69:1337/'

# POST parameter names.
USERNAME              = 'username'
EMAIL                 = 'email'
PASSWORD              = 'password'
FIRST_NAME            = 'first_name'
LAST_NAME             = 'last_name'

CONTENT               = 'content'
CUSTOM_SQL_COMMAND    = 'custom_sql_command'

NUMBER_OF_USERS       = 'number_of_users'
SPAWN_RATE            = 'spawn_rate'
TEST_DURATION         = 'test_duration'

JOINED_DATE           = 'joined_date'
PHONE_NUMBER          = 'phone_number'
CELL_CARRIER          = 'cell_carrier'
ZIP_CODE              = 'zip_code'
BIRTHDAY              = 'birthday'

MESSAGE_CONTENT       = 'content'
MESSAGE_TIME          = 'time'
MESSAGE_DATE          = 'date'

PAGE_SENT_FROM        = 'page_sent_from'

PAYMENT_PER_SHARE     = 'payment_per_share'
STOCK_TICKER_SYMBOL   = 'stock_symbol'
NUMBER_OF_SHARES      = 'number_of_shares'
PAYOUT_DATE           = 'payout_date'

TEST_NAME         = 'test_name'
TEST_RUN_TIME     = 'test_run_time'
TEST_PASS_OR_FAIL = 'test_pass_or_fail'
TEST_NOTES        = 'test_notes'
BROWSER_USED      = 'browser_used'
UNIQUE_ID         = 'unique_id'

# Field names for Selenium Testing.
PROJECT_NAME          = 'project_name'
PROJECT_DESCRIPTION   = 'project_description'
IS_FRONT_END          = 'is_front_end'
CURRENT_HOME_URL      = 'current_home_url'
TEST_NAME             = 'test_name'
TEST_DESCRIPTION      = 'test_description'
DATE_TIME_RAN         = 'date_time_ran'
SPECIFIC_TEST_RUN_IDS = 'specific_test_run_ids'
SELENIUM_TEST         = 'selenium_test'
RUN_ID                = 'run_id'
RUN_TIME              = 'run_time'
OUTPUT                = 'output'
PASSED                = 'passed'
BROWSER_USED          = 'browser_used'

# Field names for work hours logging.
START_OR_STOP_TIME = 'start_or_stop_time'
IS_START_TIME      = 'is_start_time'

# Server response messages.
SERVER_REPLY_INVALID_POST_DATA_ERROR                = HttpResponse('Invalid POST data!')
SERVER_REPLY_INVALID_NUMBER_OF_POST_ARGUMENTS_ERROR = HttpResponse('Invalid number of POST arguments!')
SERVER_REPLY_GENERIC_NO                             = HttpResponse('n')
SERVER_REPLY_GENERIC_YES                            = HttpResponse('y')
SERVER_REPLY_GENERIC_LOGIC_ERROR                    = HttpResponse('l')
SERVER_REPLY_GENERIC_SERVER_ERROR                   = HttpResponse('s')


def check_POST_arguments(arguments, request):
	"""Just a utility function to raise an exception if there is an in-correct match on POST arguments.
	:param arguments: The arguments to check for.
	:param request: Contains information regarding the request sent in.
	:return: Boolean indicating if this threw an exception or not.
	"""
	if len(request.POST) != len(arguments):
		dbg.raise_exception(dbg.MyException, 'Got ' + str(len(request.POST)) + ' number of arguments instead of ' + str(len(arguments)))
		return HttpResponse(SERVER_REPLY_INVALID_NUMBER_OF_POST_ARGUMENTS_ERROR)
	for arg in arguments:
		if arg not in request.POST:
			dbg.raise_exception(dbg.MyException, 'Argument not passed in : ' + str(arg) + '.')
			return HttpResponse(SERVER_REPLY_INVALID_POST_DATA_ERROR)
	return None

'''  ___  __   __       ___     ___       __
	|__  |__) /  \ |\ |  |     |__  |\ | |  \
	|    |  \ \__/ | \|  |     |___ | \| |__/
'''


@csrf_exempt
def login_GET(request):
	"""This handles the GET request to retrieve the login page.
	:param request: Contains information regarding the request sent in.
	:return: The login page.
	"""
	specific_page_after_login = request.GET.get('next', 'no_next')
	if specific_page_after_login == 'no_next':
		return render(request, OUTSIDE_PAGES_LOGIN, {'redirect_url': 'profile', 'build_number': NEXUS_BUILD_NUMBER})
	else:
		return render(request, OUTSIDE_PAGES_LOGIN, {'redirect_url': specific_page_after_login, 'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def profile_GET(request):
	"""This handles the GET request to retrieve the profile page.
	:param request: Contains information regarding the request sent in.
	:return: The profile page.
	"""
	current_user = request.user
	#print('Cell carrier for ' + str(current_user) + ' is ' + current_user.profile.cell_carrier + '.')
	return render(request, PERSONAL_PAGES_PROFILE, {'cell_carriers': sorted(sms_sender.cell_carriers_dictionary.keys()), 'current_cell_carrier': current_user.profile.cell_carrier, 'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def home_GET(request):
	"""This handles the GET request to retrieve the home page.
	:param request: Contains information regarding the request sent in.
	:return: The home page.
	"""
	return render(request, PERSONAL_PAGES_HOME, {'build_number': NEXUS_BUILD_NUMBER})


def register_GET(request):
	"""This handles the GET request to retrieve the register page.
	:param request: Contains information regarding the request sent in.
	:return: The register page.
	"""
	return render(request, OUTSIDE_PAGES_REGISTER)


def account_recover_GET(request):
	"""This handles the GET request to retrieve the account recover page.
	:param request: Contains information regarding the request sent in.
	:return: The account recover page.
	"""
	return render(request, OUTSIDE_PAGES_ACCOUNT_RECOVER)


@login_required(login_url=LOGIN_URL)
def logout_GET(request):
	"""This handles the GET request to logout. It will return the login page and logout the user.
	:param request: Contains information regarding the request sent in.
	:return:  The login page.
	"""
	logout(request)
	return render(request, OUTSIDE_PAGES_LOGIN)


@login_required(login_url=LOGIN_URL)
def trading_GET(request):
	"""This handles the GET request to retrieve the trading page.
	:param request: Contains information regarding the request sent in.
	:return: The trading html page.
	"""
	return render(request, PERSONAL_PAGES_TRADING, {'dividend_data': Dividend.objects.filter(user=request.user), 'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def servers_GET(request):
	"""This handles the GET request to retrieve the servers page.
	:param request: Contains information regarding the request sent in.
	:return: The servers html page.
	"""
	return render(request, ADMIN_PAGES_SERVERS, {'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def sleep_GET(request):
	"""This handles the GET request to retrieve the sleep page.
	:param request: Contains information regarding the request sent in.
	:return: The sleep HTML page.
	"""
	return render(request, PERSONAL_PAGES_SLEEP, {'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def qa_GET(request):
	"""This handles the GET request to retrieve the qa self testing page.
	:param request: Contains information regarding the request sent in.
	:return: The qa self testing HTML page.
	"""
	return render(request, QA_PAGES_SELF, {'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def nationals_GET(request):
	"""This handles the GET request to retrieve the nationals page.
	:param request: Contains information regarding the request sent in.
	:return: The nationals testing HTML page.
	"""
	return render(request, QA_PAGES_NATIONALS, {'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def qa_selenium_tests_GET(request):
	"""This handles the GET request to retrieve the qa selenium test page.
	:param request: Contains information regarding the request sent in.
	:return: The QA Selenium Tests page.
	"""
	all_selenium_tests = test_parser.get_test_functions_from_file(test_parser.QA_PATH)
	return render(request, QA_PAGES_SELF_SELENIUM_TESTS, {'number_of_selenium_tests': len(all_selenium_tests) * len(selenium_base.SUPPORTED_BROWSERS), 'all_selenium_tests': all_selenium_tests, 'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def sk_selenium_tests_GET(request):
	"""This handles the GET request to retrieve the SK page.
	:param request: Contains information regarding the request sent in.
	:return: The SK testing HTML page.
	"""
	all_selenium_tests = test_parser.get_test_functions_from_file(test_parser.SK_PATH)
	return render(request, QA_PAGES_SK, {'all_selenium_tests': all_selenium_tests, 'number_of_selenium_tests': len(all_selenium_tests) * len(selenium_base.SUPPORTED_BROWSERS), 'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def database_GET(request):
	"""This handles the GET request to retrieve the database page.
	:param request: Contains information regarding the request sent in.
	:return: The database HTML page.
	"""
	return render(request, ADMIN_PAGES_DATABASE, {'build_number': NEXUS_BUILD_NUMBER})


@login_required(login_url=LOGIN_URL)
def image_compression_GET(request):
	"""This handles the GET request to retrieve the compression page.
	:param request: Contains information regarding the request sent in.
	:return: The compression HTML page.
	"""
	return render(request, SCC_PAGES_IMAGE_COMPRESSION, {'build_number': NEXUS_BUILD_NUMBER})


def get_current_time_as_string():
	right_now = datetime.datetime.now()
	current_hour = str(right_now.time().hour)
	if len(current_hour) == 1:
		current_hour = '0' + current_hour
	current_minute = str(right_now.time().minute)
	if len(current_minute) == 1:
		current_minute = '0' + current_minute
	return current_hour + ':' + current_minute


def get_current_date_as_string():
	return datetime.datetime.strftime(datetime.datetime.now().date(), '%m/%d/%Y')


@login_required(login_url=LOGIN_URL)
def text_notifications_GET(request):
	"""This handles the GET request to retrieve the text notifications page.
	:param request: Contains information regarding the request sent in.
	:return: The text notifications HTML page.
	"""
	a = ScheduledTextNotification.objects.filter(user=request.user)

	# Find all the currently scheduled text notifications.
	print('Printing all text notifications for user :' + str(request.user))
	for p in a:
		print(p)

	return render(request, PERSONAL_PAGES_TEXT_NOTIFICATIONS, {'current_time': get_current_time_as_string(), 'current_date': get_current_date_as_string(), 'build_number': NEXUS_BUILD_NUMBER, 'scheduled_text_messages': a, 'current_text_messages': a})


@login_required(login_url=LOGIN_URL)
def work_hours_logging_GET(request):
	"""This handles the GET request to retrieve the work hours logging page.
	:param request: Contains information regarding the request sent in.
	:return: The work hours logging HTML page.
	"""
	all_work_hours = WorkHours.objects.filter(user=request.user)
	all_work_hours_for_this_week = []
	all_work_hours_outside_of_this_week = []

	today                = datetime.datetime.today()
	today                = today.replace(tzinfo=pytz.utc)
	today                = today.replace(hour=0, minute=0, second=0)
	day_of_week          = today.weekday()
	to_end_of_week       = datetime.timedelta(days=6-day_of_week)
	end_of_week          = today + to_end_of_week
	to_beginning_of_week = datetime.timedelta(days=day_of_week)
	beginning_of_week    = today - to_beginning_of_week

	this_week = {0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: []}
	days      = {0: 'Monday', 1: 'Tuesday', 2: 'Wednesday', 3: 'Thursday', 4: 'Friday', 5: 'Saturday', 6: 'Sunday'}

	#print('Printing hours!')
	for hours in all_work_hours:

		# Get the DateTime object.
		local_date_time = hours.start_or_stop_time

		if local_date_time > beginning_of_week:
			this_week[local_date_time.weekday()].append(local_date_time)
			all_work_hours_for_this_week.append(hours)
		else:
			all_work_hours_outside_of_this_week.append(hours)

	day_summary = []

	hours_worked_for_the_week = 0

	# Go through each day. e is the day number starting at 0.
	for e in this_week:
		this_week[e].sort()

		total_seconds_for_the_day = 0
		for i, t in enumerate(this_week[e]):
			if i % 2 != 0:
				delta                      = t - this_week[e][i - 1]
				total_seconds_for_the_day += delta.total_seconds()

		total_hours_worked                  = total_seconds_for_the_day / 3600.0
		hours_worked_for_the_week          += total_hours_worked
		hours_worked_as_percentage_of_total = str(int((total_hours_worked / 8.0) * 100.0))

		day_summary.append([days[e], hours_worked_as_percentage_of_total, str(total_hours_worked)])

	total_percentage = str(int((hours_worked_for_the_week / 40.0) * 100.0))

	return render(request, PERSONAL_PAGES_WORK_HOURS_LOGGING, {'total_percentage': total_percentage, 'total_hours': str(hours_worked_for_the_week), 'day_summary': day_summary, 'hours_works': all_work_hours_for_this_week, 'old_hours_worked': all_work_hours_outside_of_this_week, 'build_number': NEXUS_BUILD_NUMBER, 'current_date': get_current_date_as_string(), 'current_time': get_current_time_as_string()})


@login_required(login_url=LOGIN_URL)
def admin_GET(request):
	"""This handles the GET request to retrieve the admin page.
	:param request: Contains information regarding the request sent in.
	:return: The admin HTML page.
	"""
	return render(request, ADMIN_PAGES_ADMIN, {'feedback': Feedback.objects.all(), 'projects': Project.objects.all(), 'build_number': NEXUS_BUILD_NUMBER})


'''  __        __           ___       __
	|__)  /\  /  ` |__/    |__  |\ | |  \
	|__) /~~\ \__, |  \    |___ | \| |__/
'''


# User related!
@csrf_exempt
def verify_user_GET(request):
	"""This handles the POST request for when a user clicks on their verification link.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. In this case nothing useful will be returned lol. Just empty text.
	"""
	email = request.GET.get('email', 'no_email')
	user_to_verify = User.objects.get(email=email)
	user_to_verify.profile.verify()
	if email != 'no_email':
		sms_sender.send_sms_message('New user has verified!', 'The user is :' + email, 'thenexusserveremail@gmail.com')
		return SERVER_REPLY_GENERIC_YES
	else:
		return HttpResponse('Invalid request, please provide an email to verify.')


@csrf_exempt
def login_POST(request):
	"""This handles the POST request to login.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. In this case just a string contain success or failure status.
	"""
	if check_POST_arguments([USERNAME, PASSWORD], request) is not None:
		return check_POST_arguments([USERNAME, PASSWORD], request)

	received_username = request.POST[USERNAME]
	received_password = request.POST[PASSWORD]

	user = authenticate(username=received_username, password=received_password)

	if user is not None:
		print('Valid login combo for : ' + received_username + '!')
		login(request, user)
		return SERVER_REPLY_GENERIC_YES
	else:
		print('Login attempt failed for ' + received_username + '!')
		return SERVER_REPLY_GENERIC_NO


@csrf_exempt
def modify_account_POST(request):
	"""This handles the POST request to modify a user's information.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([PHONE_NUMBER, CELL_CARRIER, ZIP_CODE, BIRTHDAY, FIRST_NAME, LAST_NAME], request) is not None:
		return check_POST_arguments([PHONE_NUMBER, CELL_CARRIER, ZIP_CODE, BIRTHDAY, FIRST_NAME, LAST_NAME], request)

	current_user = request.user

	# TODO : Add syntax/formatting checks against these fields!
	current_user.profile.update_profile_information(cell_number=request.POST[PHONE_NUMBER], cell_carrier=request.POST[CELL_CARRIER], zip_code=request.POST[ZIP_CODE], birthday=request.POST[BIRTHDAY], first_name=request.POST[FIRST_NAME], last_name=request.POST[LAST_NAME])

	return SERVER_REPLY_GENERIC_YES


@csrf_exempt
def create_user_POST(request):
	"""This handles the POST request to create a user.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([USERNAME, EMAIL, PASSWORD], request) is not None:
		return check_POST_arguments([USERNAME, EMAIL, PASSWORD], request)

	received_username = request.POST[USERNAME]
	received_password = request.POST[PASSWORD]
	received_email    = request.POST[EMAIL]

	if User.objects.filter(username=received_username).exists():
		return SERVER_REPLY_GENERIC_SERVER_ERROR
	elif User.objects.filter(email=received_email).exists():
		return SERVER_REPLY_GENERIC_SERVER_ERROR
	else:
		print('The user ' + received_username + ' is being created and logged in!')
		sms_sender.send_sms_message('Nexus Quality Assurance Account Verification', 'Hello hello! Please click this link to send the admin a verification request (sccqa.com/verify?email=' + received_email + '). This gets sent as a text message so account creation shoudln\'t take toooo long....(╯°□°)╯︵ ┻━┻ ..... ┬─┬﻿ ノ( ゜-゜ノ).', received_email)
		user = User.objects.create_user(username=received_username, password=received_password, email=received_email)
		login(request, user)
		return SERVER_REPLY_GENERIC_YES


@csrf_exempt
def delete_account_POST(request):
	"""This handles the POST request to delete the user account.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. TODO : better documentation for this
	"""
	current_user = request.user

	if current_user is not None:
		print('Deleting the account : ' + str(current_user))
		logout(request)
		current_user.delete()
		return SERVER_REPLY_GENERIC_YES
	else:
		print('Could not delete the account ' + str(current_user) + ' as it does not exist!?!')
		return SERVER_REPLY_GENERIC_NO


@csrf_exempt
def send_recover_email_POST(request):
	"""This handles the POST request to send a recovery email.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([EMAIL], request) is not None:
		return check_POST_arguments([EMAIL], request)

	received_email = request.POST[EMAIL]

	if not User.objects.filter(email=received_email).exists():
		return SERVER_REPLY_GENERIC_NO
	else:
		print('Recovery email sent to ' + received_email + '!')
		sms_sender.send_sms_message('NQA response', 'Password recovery is not setup yet!', received_email)
		return SERVER_REPLY_GENERIC_YES


# Admin back end operations.
@csrf_exempt
def hit_peon_URL_POST(request):
	"""This handles the POST request to hit the peon server with a custom URL.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse from the peon server.
	"""
	if check_POST_arguments([PEON_URL], request) is not None:
		return check_POST_arguments([PEON_URL], request)

	print('Got the peon url of : ' + str(request.POST[PEON_URL]) + '.')
	received_peon_url = str(request.POST[PEON_URL]).replace('\n', '').replace(' ', '')

	response = requests.get('http://23.253.22.69:1337/' + received_peon_url)

	if response.status_code == 200:
		return HttpResponse(response.content)
	else:
		print('Peon server error!')
		return 'Peon server error!'


@csrf_exempt
def run_custom_SQL_POST(request):
	"""This handles the POST request to run a custom SQL command.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	if check_POST_arguments([CUSTOM_SQL_COMMAND], request) is not None:
		return check_POST_arguments([CUSTOM_SQL_COMMAND], request)

	received_sql_command = request.POST[CUSTOM_SQL_COMMAND]

	print('Received the custom SQL command : ' + str(received_sql_command))

	results = db_api.run_custom_sql_command(received_sql_command)

	print('The results were : ' + str(results))

	return str(results)


# DIVIDENDS BACK END.
@csrf_exempt
def get_dividends_data_POST(request):
	"""This handles the POST request to get dividends data.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	data = Dividend.objects.filter(user=request.user)

	if 'as_string' in request.get_full_path():
		return_string = ''
		for d in data:
			return_string += str(d)
		return HttpResponse(data)
	else:
		json_data = []
		for d in data:
			local_data = {'payout_date': int(time.mktime(d.payout_date.timetuple())), 'number_of_shares': d.number_of_shares, 'pay_per_share': d.pay_per_share, 'share_ticker': d.share_ticker}
			json_data.append(local_data)

		return JsonResponse(json_data, safe=False)


@csrf_exempt
def add_dividend_payment_POST(request):
	"""This handles the POST request to add a dividend payment.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	if check_POST_arguments([PAYMENT_PER_SHARE, STOCK_TICKER_SYMBOL, NUMBER_OF_SHARES, PAYOUT_DATE], request) is not None:
		return check_POST_arguments([PAYMENT_PER_SHARE, STOCK_TICKER_SYMBOL, NUMBER_OF_SHARES, PAYOUT_DATE], request)

	received_number_of_shares  = request.POST[NUMBER_OF_SHARES]
	received_payment_per_share = request.POST[PAYMENT_PER_SHARE]
	received_stock_symbol      = request.POST[STOCK_TICKER_SYMBOL]
	received_payout_date       = request.POST[PAYOUT_DATE]
	segments = received_payout_date.split('/')
	received_payout_date = segments[2] + '-' + segments[0] + '-' + segments[1]

	dividend = Dividend(user=request.user, payout_date=received_payout_date, number_of_shares=received_number_of_shares, pay_per_share=received_payment_per_share, share_ticker=received_stock_symbol)
	dividend.save()

	return SERVER_REPLY_GENERIC_YES


@csrf_exempt
def remove_dividend_payment_POST(request):
	"""This handles the POST request to remove a dividend payment.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	if check_POST_arguments([STOCK_TICKER_SYMBOL, PAYOUT_DATE], request) is not None:
		return check_POST_arguments([STOCK_TICKER_SYMBOL, PAYOUT_DATE], request)

	received_stock_symbol = request.POST[STOCK_TICKER_SYMBOL]
	received_payout_date  = request.POST[PAYOUT_DATE]

	transaction = Dividend.objects.filter(payout_date=received_payout_date, share_ticker=received_stock_symbol)
	transaction.delete()


# LOCUST BACK END.
@csrf_exempt
def is_locust_currently_running_GET(request):
	if locust_api.is_locust_currently_running():
		return SERVER_REPLY_GENERIC_YES
	else:
		return SERVER_REPLY_GENERIC_NO


@csrf_exempt
def get_current_table_data_GET(request):
	try:
		data = locust_api.get_current_table_data()
		return data
	except Exception as e:
		print('Exception happened when trying to get currently table data for QA page.')
		return JsonResponse({'data': []})


@csrf_exempt
def launch_swarming_POST(request):
	if check_POST_arguments([NUMBER_OF_USERS, SPAWN_RATE, TEST_DURATION], request) is not None:
		return check_POST_arguments([NUMBER_OF_USERS, SPAWN_RATE, TEST_DURATION], request)

	received_number_of_users = request.POST[NUMBER_OF_USERS]
	received_spawn_rate      = request.POST[SPAWN_RATE]
	received_test_duration   = request.POST[TEST_DURATION]

	print('Just got an order to launch swarming, here are the parameters : [' + str(received_number_of_users) + ', ' + str(received_spawn_rate) + ', ' + str(received_test_duration) + ']')

	locust_api.launch_swarming(received_number_of_users, received_spawn_rate, received_test_duration)
	return SERVER_REPLY_GENERIC_YES


# For text notification scheduling.
@csrf_exempt
def schedule_message_POST(request):
	"""This handles the POST request to schedule a text notification message.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([MESSAGE_CONTENT, MESSAGE_TIME, MESSAGE_DATE], request) is not None:
		return check_POST_arguments([MESSAGE_CONTENT, MESSAGE_TIME, MESSAGE_DATE], request)

	current_user = request.user

	received_content = str(request.POST[MESSAGE_CONTENT])
	received_time    = str(request.POST[MESSAGE_TIME])
	received_date    = str(request.POST[MESSAGE_DATE])

	#print('This is the time that I got: ' + str(received_time))
	#print('This is the date that I got: ' + str(received_date))

	# TODO : Do more error checking here.

	year   = int(received_date.split('/')[2])
	day    = int(received_date.split('/')[1])
	month  = int(received_date.split('/')[0])

	hour   = int(received_time.split(':')[0])
	minute = int(received_time.split(':')[1])

	created_at_time = datetime.datetime.now()

	scheduled_text_notification = ScheduledTextNotification(user=current_user, message_to_send=received_content, created_at=created_at_time, execute_at=created_at_time.replace(year=year, month=month, day=day, hour=hour, minute=minute))
	scheduled_text_notification.save_and_schedule_message()

	return SERVER_REPLY_GENERIC_YES


@csrf_exempt
def verify_message_was_sent_POST(request):
	"""This handles the POST request to verify that a text notification message was sent.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([UNIQUE_ID], request) is not None:
		return check_POST_arguments([UNIQUE_ID], request)

	received_id  = request.POST[UNIQUE_ID]

	print('Just got a verification message!')
	print('ID is : ' + str(received_id))

	scheduled_text_notification = ScheduledTextNotification.objects.get(id=received_id)
	scheduled_text_notification.verify()

	return SERVER_REPLY_GENERIC_YES


# For QA/General Selenium.
class TestResult:
	"""
	Outlines the information contained in a single test result.
	"""

	def __init__(self, name, browser_used, run_time, passed, notes):
		self.test_name    = name
		self.browser_used = browser_used
		self.run_time     = run_time
		self.passed       = passed
		self.notes        = notes


class TestSuite:
	"""
	Outlines the information contained in a single test suite.
	"""

	def __init__(self, unique_id, project_name):
		self.unique_id    = unique_id
		self.project_name = project_name


class TestSuiteCache:
	"""
	Manages cache information for users and selenium tests run.
	"""

	def __init__(self):
		# This will be a double dictionary. The first layer of key:pair is {user_name:dictionary_of_test_suites}.
		# The dictionary_of_test_suites if composed of {test_suite_unique_number:list_of_selenium_tests}.
		self.cache_for_users_and_test_suites = {}

	def add_new_test_suite(self, username, unique_id, project_name):
		"""This will add a new test suite to keep track of.
		:param username:     The name of the user.
		:param unique_id:    The unique id of the test suite.
		:param project_name: The project name that this suite is testing.
		:return: Void.
		"""
		if username not in self.cache_for_users_and_test_suites:
			self.cache_for_users_and_test_suites[username] = {}
		if unique_id not in self.cache_for_users_and_test_suites[username]:
			self.cache_for_users_and_test_suites[username][unique_id] = {}

	def add_new_test_result(self, test_result, unique_id):
		"""This will add a new test result to keep track of.
		:param test_result: The results of this specific test.
		:param unique_id: The unique id of the test suite.
		:return: Void.
		"""
		# Find the test suite.
		user_match = None
		for user_key in self.cache_for_users_and_test_suites.keys():
			if unique_id in self.cache_for_users_and_test_suites[str(user_key)]:
				user_match = str(user_key)
				break
		#print('--------------------------------------------------------------------------------------------------------------------------------')
		#print('THE USER MATCH IS : {' + str(user_match) + '}')
		#rint('--------------------------------------------------------------------------------------------------------------------------------')

		if str(user_match) is None:
			dbg.terminate('No match!')

		if test_result.test_name not in self.cache_for_users_and_test_suites[user_match][unique_id]:
			self.cache_for_users_and_test_suites[user_match][unique_id][test_result.test_name] = {}
		self.cache_for_users_and_test_suites[user_match][unique_id][test_result.test_name][test_result.browser_used] = [False, test_result.run_time, test_result.passed, test_result.notes]

	def fetch_test_results(self, username, unique_id):
		"""This will return the data of a test suite that has not been sent yet.
		:param username: The username to get the test suite from.
		:param unique_id: Tue unique ID of the test suite to get single test results from.
		:return: The results from the test suite.
		"""

		#print('---------------------------------------------------------------------------------------')
		#print(username)
		#print(unique_id)
		#print(self.cache_for_users_and_test_suites)
		#print('#######################################################################################')
		#for user in self.cache_for_users_and_test_suites:
		#	print('Printing cache information for user : ' + str(user))
		#	for ts in self.cache_for_users_and_test_suites[user]:
		#		print('Printing cache information for test suite : ' + str(ts))
		#		for t in self.cache_for_users_and_test_suites[user][ts]:
		#			print('Printing cache information for test : ' + str(t))
		#			for st in self.cache_for_users_and_test_suites[user][ts][t]:
		#				print(str(st) + str(self.cache_for_users_and_test_suites[user][ts][t][st]))

		#for k in self.cache_for_users_and_test_suites:
		#	print(str(k) + '\t' + str(self.cache_for_users_and_test_suites[k]))
		#print('---------------------------------------------------------------------------------------')

		data_to_return = []
		if username in self.cache_for_users_and_test_suites:
			if unique_id in self.cache_for_users_and_test_suites[username]:
				# Go through each test.
				for test_name in self.cache_for_users_and_test_suites[username][unique_id]:
					# Go through each browser variation of that test.
					for b in self.cache_for_users_and_test_suites[username][unique_id][test_name]:

						#print('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
						#print(self.cache_for_users_and_test_suites[username][unique_id][test_name][b])
						#print(self.cache_for_users_and_test_suites[username][unique_id][test_name][b][0])
						#print('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')

						if not self.cache_for_users_and_test_suites[username][unique_id][test_name][b][0]:

							v0 = self.cache_for_users_and_test_suites[username][unique_id][test_name][b][1]
							v1 = self.cache_for_users_and_test_suites[username][unique_id][test_name][b][2]
							v2 = self.cache_for_users_and_test_suites[username][unique_id][test_name][b][3]

							#data_to_return[test_name] = [b, v0, v1, v2]
							data_to_return.append([test_name, b, v0, v1, v2])

							# Mark this value as returned.
							self.cache_for_users_and_test_suites[username][unique_id][test_name][b][0] = True

				return data_to_return
			else:
				return {}
		else:
			return {}

	def get_test_results_for_specific_test_suite(self, username, unique_id):
		"""This will return the data of a specific test suite.
		:param username: The username to get the test suite from.
		:param unique_id: The unique ID of the test suite to get.
		:return: The results from the test suite.
		"""
		if username in self.cache_for_users_and_test_suites:
			if unique_id in self.cache_for_users_and_test_suites[username]:
				return self.cache_for_users_and_test_suites[username][unique_id]
			else:
				return {}
		else:
			return {}


test_suite_cache = TestSuiteCache()


@csrf_exempt
def get_selenium_project_data_POST(request):
	"""This handles the POST request to get selenium history data from the PEON server.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	if check_POST_arguments([PROJECT_NAME], request) is not None:
		return check_POST_arguments([PROJECT_NAME], request)

	# TODO : Add a check that this request was specifically sent from the PEON server.

	response = requests.post(PEON_IP_URL + 'get_selenium_project_data', data={PROJECT_NAME: request.POST[PROJECT_NAME]})
	return JsonResponse(ast.literal_eval(response.content.decode('utf-8')), safe=False)


@csrf_exempt
def get_test_suite_number_POST(request):
	"""This handles the POST request to get a test suite number from the PEON server.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	if check_POST_arguments([PROJECT_NAME], request) is not None:
		return check_POST_arguments([PROJECT_NAME], request)

	received_project_name = request.POST[PROJECT_NAME]

	global test_suite_cache
	response = requests.post(PEON_IP_URL + 'get_test_suite_number', data={PROJECT_NAME: received_project_name})
	if response.status_code == 200:
		unique_id = response.content.decode('utf-8')
		print('Got the test suite number of ' + str(unique_id) + '!')
		test_suite_cache.add_new_test_suite(username=str(request.user), unique_id=unique_id, project_name=received_project_name)
		return HttpResponse(unique_id)
	else:
		return SERVER_REPLY_GENERIC_NO


@csrf_exempt
def launch_test_suite_POST(request):
	"""This handles the POST request to launch a test suite on the PEON server.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	if check_POST_arguments([UNIQUE_ID, PROJECT_NAME], request) is not None:
		return check_POST_arguments([UNIQUE_ID, PROJECT_NAME], request)

	response = requests.post(PEON_IP_URL + 'launch_test_suite', data={UNIQUE_ID: request.POST[UNIQUE_ID], PROJECT_NAME: request.POST[PROJECT_NAME]})
	if response.status_code == 200:
		return SERVER_REPLY_GENERIC_YES
	else:
		print('Error with PEON!')
		print(response.content)
		return SERVER_REPLY_GENERIC_NO


@csrf_exempt
def single_test_data_POST(request):
	"""This handles the POST request to add test data into the server's cache.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	if check_POST_arguments([UNIQUE_ID, TEST_NAME, TEST_RUN_TIME, TEST_PASS_OR_FAIL, TEST_NOTES, BROWSER_USED], request) is not None:
		return check_POST_arguments([UNIQUE_ID, TEST_NAME, TEST_RUN_TIME, TEST_PASS_OR_FAIL, TEST_NOTES, BROWSER_USED], request)

	received_test_name         = request.POST[TEST_NAME]
	received_test_run_time     = request.POST[TEST_RUN_TIME]
	received_test_pass_or_fail = request.POST[TEST_PASS_OR_FAIL]
	received_unique_id         = request.POST[UNIQUE_ID]
	received_test_notes        = request.POST[TEST_NOTES]
	received_browser_used      = request.POST[BROWSER_USED]

	# TODO : Add a check that the PEON was the one that sent the request.

	global test_suite_cache
	test_result = TestResult(name=received_test_name, browser_used=received_browser_used, run_time=received_test_run_time, passed=received_test_pass_or_fail, notes=received_test_notes)
	test_suite_cache.add_new_test_result(test_result, received_unique_id)

	return SERVER_REPLY_GENERIC_YES


@csrf_exempt
def fetch_suite_data_POST(request):
	"""This handles the POST request to get any newly generated table data.
	:param request: Contains information regarding the request sent in.
	:return: Any new data in a JSON_Response.
	"""
	if check_POST_arguments([UNIQUE_ID], request) is not None:
		return check_POST_arguments([UNIQUE_ID], request)

	global test_suite_cache
	data = test_suite_cache.fetch_test_results(str(request.user), request.POST[UNIQUE_ID])
	return JsonResponse(data, safe=False)


@csrf_exempt
def get_table_data_POST(request):
	"""This handles the POST request to get the current table data.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse.
	"""
	if check_POST_arguments([UNIQUE_ID], request) is not None:
		return check_POST_arguments([UNIQUE_ID], request)

	global test_suite_cache
	data = test_suite_cache.get_test_results_for_specific_test_suite(str(request.user), request.POST[UNIQUE_ID])
	return JsonResponse(data, safe=False)


@csrf_exempt
def add_project_POST(request):
	"""This handles the POST request to add a project.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([PROJECT_NAME, PROJECT_DESCRIPTION, CURRENT_HOME_URL], request) is not None:
		return check_POST_arguments([PROJECT_NAME, PROJECT_DESCRIPTION, CURRENT_HOME_URL], request)

	received_project_name        = request.POST[PROJECT_NAME]
	received_project_description = request.POST[PROJECT_DESCRIPTION]
	received_current_home_url    = request.POST[CURRENT_HOME_URL]

	project = Project(project_name=received_project_name, project_description=received_project_description, current_home_url=received_current_home_url)
	project.save()

	return SERVER_REPLY_GENERIC_YES


# For work hours logging.
@csrf_exempt
def remove_work_hours_POST(request):
	"""This handles the POST request to delete work hours.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([UNIQUE_ID], request) is not None:
		return check_POST_arguments([UNIQUE_ID], request)

	received_unique_id = request.POST[UNIQUE_ID]

	work_hours = WorkHours.objects.filter(unique_id=received_unique_id)
	work_hours.delete()

	return SERVER_REPLY_GENERIC_YES


'''
@csrf_exempt
def get_work_hours_POST(request):
	"""This handles the POST request to get the current user's work hours.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	data = []
	original_data = WorkHours.objects.filter(user=request.user)
	for d in original_data:
		data.append({'start_or_stop_time': d.start_or_stop_time, 'is_start_time': d.is_start_time})

	return JsonResponse(data)
'''


@csrf_exempt
def add_work_hours_POST(request):
	"""This handles the POST request to add work hours.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([START_OR_STOP_TIME, IS_START_TIME], request) is not None:
		return check_POST_arguments([START_OR_STOP_TIME, IS_START_TIME], request)

	received_start_or_stop_time = request.POST[START_OR_STOP_TIME]

	local_date = received_start_or_stop_time.split(',')[0]
	local_time = received_start_or_stop_time.split(',')[1]

	year  = int(local_date.split('/')[2])
	month = int(local_date.split('/')[0])
	day   = int(local_date.split('/')[1])

	hour   = int(local_time.split(':')[0])
	minute = int(local_time.split(':')[1])

	print('Hour: ' + str(hour))
	print('Minute: ' + str(minute))

	#date_time_object = datetime.datetime(year=year, month=month, day=day, hour=hour, minute=minute, tzinfo=pytz.timezone('America/Chicago'))
	date_time_object = datetime.datetime(year=year, month=month, day=day, hour=hour, minute=minute)
	print(date_time_object)

	#print('Going to add the following date time object : ')
	#print(str(date_time_object))

	received_is_start_time = request.POST[IS_START_TIME]
	if received_is_start_time == 'false' or received_is_start_time == '\'false\'':
		received_is_start_time = False
	elif received_is_start_time == 'true' or received_is_start_time == '\'true\'':
		received_is_start_time = True

	#print('Trying to add work hours!')
	#print(received_start_or_stop_time)
	#print(received_is_start_time)

	work_hours = WorkHours(user=request.user, start_or_stop_time=date_time_object, is_start_time=received_is_start_time)
	work_hours.save()

	return SERVER_REPLY_GENERIC_YES


# For Feedback.
@csrf_exempt
def send_feedback_POST(request):
	"""This handles the POST request to send feedback.
	:param request: Contains information regarding the request sent in.
	:return: The HttpResponse. Just a server_reply_response_string.
	"""
	if check_POST_arguments([CONTENT, PAGE_SENT_FROM], request) is not None:
		return check_POST_arguments([CONTENT, PAGE_SENT_FROM], request)

	received_feedback       = request.POST[CONTENT]
	received_page_sent_from = request.POST[PAGE_SENT_FROM]

	feedback = Feedback(user=request.user, content=received_feedback, sent_at=datetime.datetime.now(), page_sent_from=received_page_sent_from)
	feedback.save()

	return SERVER_REPLY_GENERIC_YES


# SCC
def log_request(request):
	with open('requests_logging.log', 'a') as f:
		f.write(str(datetime.datetime.now()))
		f.write(str(request.body))
		f.write(str(request.body))
		f.write(str(request.path))
		f.write(str(request.path_info))
		f.write(str(request.method))
		f.write(str(request.META))
		f.write(str('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'))

@csrf_exempt
def launch_functional_tests_for_sk_POST(request):
	log_request(request)
	return HttpResponse('This URL doesn\'t do anything yet :D')

@csrf_exempt
def launch_functional_tests_for_qa_POST(request):
	log_request(request)
	return HttpResponse('This URL doesn\'t do anything yet :D')

@csrf_exempt
def launch_functional_tests_for_nationals_POST(request):
	log_request(request)
	return HttpResponse('This URL doesn\'t do anything yet :D')

@csrf_exempt
def general_request(request):
	log_request(request)
	return HttpResponse('This URL doesn\t do anything yet :D')

#
@csrf_exempt
def send_data_to_flax_POST(request):
	if check_POST_arguments(['qa_data'], request) is not None:
		return check_POST_arguments(['qa_data'], request)

	# qa_data
	response = requests.post(PEON_IP_URL + 'send_data_to_flax', data={'qa_data': request.POST['qa_data']})

	if response.status_code == 200:
		print('Peon got the message to send data to flax!')
	else:
		print('Peon did not get the message to send data to flax!')

	return SERVER_REPLY_GENERIC_YES
