from selenium import webdriver
from selenium.webdriver.remote import webelement
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
from webdriver_manager.firefox import GeckoDriverManager
import configparser
import os
import sys
import datetime
import argparse

# TODO factor out common code (defaults, login, etc) from progress_reports script
GECKO_DRIVER = 'gecko'
CHROME_DRIVER = 'chrome'
DEFAULT_CONFIG_PATH = './config.ini'

DEFAULT_WORKING_DIRECTORY = './tmp'
DEFAULT_APP_URL = 'https://app.flightschedulepro.com'
DEFAULT_TIMEOUT = 10 # seconds
DEFAULT_DAY_LOOKAHEAD = 2
DEFAULT_MAX_COMMENTS = 5


parser = argparse.ArgumentParser(
                    prog='FSP Make Reservations',
                    description='Reserves flights in advance',
                    epilog='Kill urself')
parser.add_argument('-c', '--config', default=DEFAULT_CONFIG_PATH)
parser.add_argument('-s', '--show_browser', help='Show browser window', action='store_true')
parser.add_argument('-d', '--days', help='Number of days in advance to place reservations for', default=DEFAULT_DAY_LOOKAHEAD)
parser.add_argument('-D', '--dry_run', help='Disables saving of comments', action='store_true')

args = parser.parse_args()

config = configparser.ConfigParser()

config.read(args.config)
working_dir = config['main'].get('working_directory', DEFAULT_WORKING_DIRECTORY)
app_url = config['main'].get('app_url', DEFAULT_APP_URL)
timeout = config['main'].get('timeout', DEFAULT_TIMEOUT)
tenant_id = config['main'].get('tenant_id')
if tenant_id is None or tenant_id == "":
    print('No Tenant ID configured!')
    sys.exit(1)

# HACK maybe Ubuntu specific 
if config['main'].getboolean('use_working_dir_for_tmp', False):
    os.environ["TMPDIR"] = working_dir


driver_type = config['main']['driver']
# TODO only firefox for now
if driver_type == GECKO_DRIVER:
    opt = webdriver.FirefoxOptions()
    if config['firefox'].get('binary_path') is not None:
        opt.binary_location = config['firefox'].get('binary_path')
    if not args.show_browser:
        opt.add_argument('-headless')
    # Force window size to avoid issues with responsive design
    opt.add_argument("--width=1920")
    opt.add_argument("--height=1080")
    # Disable print dialog. Causes race conditions with window switching
    firefox_profile = FirefoxProfile()
    firefox_profile.set_preference("print.enabled", False)
    opt.profile = firefox_profile
    driver = webdriver.Firefox(options=opt, service=FirefoxService(GeckoDriverManager().install()))
else:
    print(f'unsupported driver type "{driver_type}"')
    sys.exit(1)

# optionally prints a message and cleans up before exiting
def die(message=None):
    if message is not None:
        print(message)
    driver.quit()
    sys.exit(0)

login_url = app_url + '/Account/Login?company='+ tenant_id
driver.get(login_url)

# wait for stupid cookie modal to load and accept all ¯\_(ツ)_/¯
try:
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.ID, 'onetrust-accept-btn-handler'))
    )
    cookie_btn = driver.find_element(By.ID, 'onetrust-accept-btn-handler')
    cookie_btn.click()
except:
    pass

# Login
form = driver.find_element(By.CLASS_NAME, 'account-form')
username = driver.find_element(By.ID, 'username')
username.send_keys(config['main']['username'])
username.submit()

try:
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.ID, 'password'))
    )
except TimeoutException:
    if len(driver.find_elements(By.XPATH, '//div[@id="alerts"]')) > 0:
        die('Bad username')
    die('unknown authentication error')

password = driver.find_element(By.ID, 'password')
password.send_keys(config['main']['password'])

password.submit()

# MFA MODAL CHECK
try:
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.XPATH, '//button[@data-cy="remind-me-next-time-mfa"]'))
    )
    mfa_btn = driver.find_element(By.XPATH, '//button[@data-cy="remind-me-next-time-mfa"]')
    mfa_btn.click()
except:
    pass

# Wait for app to load and naviagte to Reservations "page"
try:
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.CLASS_NAME, 'fsp-main-drawer-content'))
    )
except TimeoutException:
    if  len(driver.find_elements(By.XPATH, '//div[@id="alerts"]')) > 0:
        die('Bad password')
    die('unknown authentication error')

today = datetime.datetime.today()
def get_reservation_rows():
    driver.get(app_url + '/App/Reservations')
    # Wait for "Future" button and click
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.XPATH, '//a[@id="mat-tab-link-1"]'))
    )
    driver.find_element(By.XPATH, '//a[@id="mat-tab-link-1"]').click()

    #Wait for rows to load
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.XPATH, '//tr[contains(@class, "cdk-row")]'))
    )
    reservation_rows = driver.find_elements(By.XPATH, '//tr[contains(@class, "cdk-row")]')

    reservation_rows = list(filter(filter_row_by_date, reservation_rows))
    return reservation_rows

def filter_row_by_date(reservation_row: webelement.WebElement) -> bool:
    instructor = reservation_row.find_element(By.XPATH, './td[contains(@class, "cdk-column-instructor")]').get_attribute("innerText")
    # Double check we're only commenting on your reservations. Out of caution abort if we see any other instructors
    if "Jason Vitale" not in instructor:
        die("WARNING: retrieved reservation for another instructor. Aborting...")
        return False
    date_str = reservation_row.find_element(By.XPATH, './td[contains(@class, "cdk-column-date")]').get_attribute("innerText")
    date = datetime.datetime.strptime(date_str, '%a, %b %d, %Y')
    if date.date() == (today + datetime.timedelta(days=args.days)).date():
        return True
    return False

rows = get_reservation_rows()

row_count = len(rows)
if row_count > DEFAULT_MAX_COMMENTS:
    die('Found more than ' + str(DEFAULT_MAX_COMMENTS) + ' reservations. Aborting...')
print('found ' + str(row_count) + ' reservations to add reservation comment to')
for i in range(0, row_count):
    row = rows[i]
    student_name = row.find_element(By.XPATH, './td[contains(@class, "cdk-column-Customer")]').get_attribute("innerText")
    reservation_date = row.find_element(By.XPATH, './td[contains(@class, "cdk-column-date")]').get_attribute("innerText")
    print('adding reservation comment for student ' + student_name + ' on ' + reservation_date)
    row.find_element(By.XPATH, './/button[contains(@class, "fsp-button")]').click()

    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.ID, 'ReservationModal-StandardViewCtrl')),
    )
    modal = driver.find_element(By.ID, 'ReservationModal-StandardViewCtrl')
    #Wait for modal/comments sections to load 
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.XPATH, '//div[contains(@ng-model, "reservationDetails.comments")]')),
    )
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.XPATH, '//a[contains(@ng-click, "editComment()")]')),
    )

    comments = modal.find_element(By.XPATH, './/div[contains(@ng-model, "reservationDetails.comments")]')
    edit_btn = comments.find_element(By.XPATH, './/a[contains(@ng-click, "editComment()")]')
    WebDriverWait(driver, timeout).until(
        EC.element_to_be_clickable(edit_btn)
    )
    edit_btn.click()
    #Wait for modal/comments sections to load 
    WebDriverWait(driver, timeout).until(
        EC.presence_of_all_elements_located((By.ID, 'comments'))
    )
    textarea = comments.find_element(By.ID, 'comments')

    textarea.clear()
    comment_text = "c172 " + today.strftime('%d %b %H%M')
    print('comment text: ' + comment_text)
    textarea.send_keys(comment_text)

    if args.dry_run is False:
        save_btn = comments.find_element(By.XPATH, './/button[contains(@ng-click, "save()")]')
        WebDriverWait(driver, timeout).until(
            EC.element_to_be_clickable(save_btn)
        )
        print("saving reservation comment")
        save_btn.click()

    driver.find_element(By.XPATH, '//button[contains(@ng-click, "dismissModal()")]').click()
    rows = get_reservation_rows()
    # If count of reservation rows changes, something is wrong
    if len(rows) is not row_count:
        die('reservation row count changed, aborting...')

die()