import datetime

from django.urls import reverse
from django.utils import timezone
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import NoSuchElementException
import pytest

from wizer import models


def test_activity_page__complete(import_one_activity, live_server, webdriver):
    import_one_activity("cycling_bad_schandau.fit")

    activity = models.Activity.objects.get()
    pk = activity.pk
    webdriver.get(live_server.url + f"/activity/{pk}")

    table_header = [cell.text for cell in webdriver.find_elements_by_tag_name("th")]
    assert "  Duration:  4:59 h" in table_header
    assert "  Distance:  44.96 km" in table_header
    assert "  Calories:  1044 kcal" in table_header
    assert "Time" in table_header
    assert "Distance" in table_header
    assert "Pace" in table_header
    assert "Label" in table_header

    assert webdriver.find_element_by_tag_name("h3").text == "Noon Cycling in Bad Schandau  "

    headings = [h.text for h in webdriver.find_elements_by_tag_name("h5")]
    assert "Fastest Sections  " in headings
    assert "Speed" in headings
    assert "Pace" in headings
    assert "Temperature" in headings
    assert "Laps" in headings

    links = [a.text for a in webdriver.find_elements_by_tag_name("a")]
    assert "  Add Activity" in links
    assert "  Workoutizer  " in links
    assert "+" in links
    assert "−" in links
    assert "Leaflet" in links
    assert "OpenStreetMap" in links

    ps = [p.text for p in webdriver.find_elements_by_tag_name("p")]
    assert "Aug 29, 2020, 17:12" in ps

    spans = [a.text for a in webdriver.find_elements_by_tag_name("span")]
    assert "Streets" in spans
    assert "Topo" in spans
    assert "Terrain" in spans
    assert "Satellite" in spans

    # check that icons are present
    assert len(webdriver.find_elements_by_class_name("fa-trophy")) > 0
    assert len(webdriver.find_elements_by_class_name("fa-fire")) > 0
    assert len(webdriver.find_elements_by_class_name("fa-road")) > 0
    assert len(webdriver.find_elements_by_class_name("fa-history")) > 0

    # check that map is displayed
    assert (
        webdriver.find_element_by_id("map").text
        == "Streets\nTopo\nTerrain\nSatellite\n+\n−\n2 km\nLeaflet | Map data: © OpenStreetMap"
    )

    # check that bokeh plots are available
    assert webdriver.find_element_by_class_name("bk-root").text == "Show Laps"
    assert len(webdriver.find_elements_by_class_name("bk-canvas")) == 3


def test_edit_activity_page(import_one_activity, live_server, webdriver, insert_sport):
    # add some sports
    insert_sport("Cycling")
    insert_sport("MTB")
    assert models.Sport.objects.count() == 2

    # activity will be mapped to sport "Cycling"
    import_one_activity("cycling_bad_schandau.fit")

    activity = models.Activity.objects.get()
    pk = activity.pk
    assert activity.sport.name == "Cycling"

    # save initial date
    initial_date = activity.date

    webdriver.get(live_server.url + f"/activity/{pk}")

    # verify that activity has some awards
    assert len(webdriver.find_elements_by_class_name("fa-trophy")) > 0
    # because it currently evaluates for awards there is no exclamation circle icon
    assert len(webdriver.find_elements_by_class_name("fa-exclamation-circle")) == 0

    # go to edit activity page by clicking the edit button
    button = webdriver.find_element_by_id("edit-activity")
    button.click()

    # verify url got changed to activity view
    assert webdriver.current_url == f"{live_server.url}/activity/{pk}/edit/"

    assert activity.name == "Noon Cycling in Bad Schandau"
    assert activity.duration == datetime.timedelta(seconds=17970)
    assert activity.is_demo_activity is False
    assert activity.evaluates_for_awards is True
    # make activity a demo activity to verify it won't get changed back
    activity.is_demo_activity = True
    activity.save()
    assert activity.is_demo_activity is True

    assert webdriver.find_element_by_tag_name("h3").text == "Edit Activity: Noon Cycling in Bad Schandau (Cycling)"
    assert webdriver.find_element_by_tag_name("button").text == "  Save"
    assert (
        webdriver.find_element_by_tag_name("form").text
        == "Activity Name:\nSport:\nCycling\nMTB\nDate:\nDuration:\n  min\nDistance:\n  km\nDescription:\nConsider this "
        "Activity for Awards:\n  \nLap Data\n\n\n  Save\nCancel\n  Delete"
    )

    links = [link.text for link in webdriver.find_elements_by_tag_name("a")]
    assert "  Add Activity" in links
    assert "  Workoutizer  " in links
    assert "Lap Data" in links  # means activity has some laps
    assert "Cancel" in links
    assert "  Delete" in links

    # uncheck the box for evaluates_for_awards
    webdriver.find_element_by_id("id_evaluates_for_awards").click()

    # enter a different name
    name_field = webdriver.find_element_by_css_selector("#id_name")
    name_field.clear()
    name_field.send_keys("Dummy Activity Name")

    # change duration
    duration_field = webdriver.find_element_by_css_selector("#id_duration")
    duration_field.clear()
    duration_field.send_keys("01:11:11")

    # change sport to verify dropdown also works
    dropdown = webdriver.find_element(By.ID, "id_sport")
    dropdown.find_element(By.XPATH, "//option[. = 'MTB']").click()

    # modify the date of the activity to verify the datetime picker widget works
    webdriver.find_element(By.ID, "id_date").click()
    webdriver.find_element(By.CSS_SELECTOR, "tr:nth-child(4) > .day:nth-child(5)").click()
    webdriver.find_element(By.CSS_SELECTOR, "tr:nth-child(3) > .day:nth-child(3)").click()
    webdriver.find_element(By.CSS_SELECTOR, ".glyphicon-time").click()
    webdriver.find_element(By.CSS_SELECTOR, "td:nth-child(3) .glyphicon-chevron-down").click()
    webdriver.find_element(By.CSS_SELECTOR, ".glyphicon-remove").click()

    # also edit lap data
    webdriver.find_element(By.ID, "edit-lap-data").click()
    lap_input_0 = webdriver.find_element(By.ID, "id_form-0-label")
    lap_input_0.clear()
    lap_input_0.send_keys("lap label 0")

    # verify that there is no second element available
    with pytest.raises(
        NoSuchElementException, match='Message: Unable to locate element: |[id="id_form\-1-label"]'  # noqa: W605
    ):
        webdriver.find_element(By.ID, "id_form-1-label")

    # submit form with modified data
    button = webdriver.find_element_by_id("submit-button")
    button.click()

    # verify url got changed to activity view
    assert webdriver.current_url == f"{live_server.url}/activity/{pk}"

    # check that all trophies got removed, since activity no longer evaluates for awards
    assert len(webdriver.find_elements_by_class_name("fa-trophy")) == 0
    assert len(webdriver.find_elements_by_class_name("fa-exclamation-circle")) == 1

    # verify attributes got changed
    activity = models.Activity.objects.get()
    assert activity.name == "Dummy Activity Name"
    assert activity.duration == datetime.timedelta(seconds=4271)
    assert activity.is_demo_activity is True
    assert activity.evaluates_for_awards is False
    assert activity.sport.name == "MTB"
    assert activity.date != initial_date

    # verify that lap label got changed
    laps = models.Lap.objects.filter(trace=activity.trace_file, trigger="manual")
    assert laps[0].label == "lap label 0"


def test_add_activity_page(insert_sport, webdriver, live_server):
    # insert some sports
    insert_sport("Cycling")
    insert_sport("MTB")
    assert models.Sport.objects.count() == 2

    webdriver.get(live_server.url)
    webdriver.find_element(By.ID, "add-activity-button").click()
    assert webdriver.current_url == live_server.url + reverse("add-activity")

    # set name
    webdriver.find_element(By.ID, "id_name").click()
    webdriver.find_element(By.ID, "id_name").clear()
    webdriver.find_element(By.ID, "id_name").send_keys("Dummy Activity")
    webdriver.find_element(By.CSS_SELECTOR, "form").click()
    # set sport
    dropdown = webdriver.find_element(By.ID, "id_sport")
    dropdown.find_element(By.XPATH, "//option[. = 'Cycling']").click()
    # set date
    webdriver.find_element(By.ID, "id_date").click()
    webdriver.find_element(By.CSS_SELECTOR, ".glyphicon-time").click()
    # minus one hour
    webdriver.find_element(By.CSS_SELECTOR, "td:nth-child(1) .glyphicon-chevron-down").click()
    # plus one minute
    webdriver.find_element(By.CSS_SELECTOR, "td:nth-child(3) .glyphicon-chevron-up").click()
    webdriver.find_element(By.ID, "id_date").click()
    webdriver.find_element(By.CSS_SELECTOR, ".input-group").click()
    # set duration
    webdriver.find_element(By.ID, "id_duration").click()
    webdriver.find_element(By.ID, "id_duration").clear()
    webdriver.find_element(By.ID, "id_duration").send_keys("00:31:00")
    # set distance
    webdriver.find_element(By.ID, "id_distance").click()
    webdriver.find_element(By.ID, "id_distance").send_keys("2.3")
    # set description
    webdriver.find_element(By.ID, "id_description").click()
    webdriver.find_element(By.ID, "id_description").send_keys("super sport")
    webdriver.find_element(By.CSS_SELECTOR, "form").click()
    # submit and wait
    webdriver.find_element(By.ID, "submit-button").click()
    WebDriverWait(webdriver, 3).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".alert")))

    assert webdriver.current_url == live_server.url + reverse("home")
    assert "Successfully added 'Dummy Activity'" in webdriver.find_element(By.CSS_SELECTOR, ".alert").text

    assert models.Activity.objects.count() == 1
    activity = models.Activity.objects.get()
    assert activity.name == "Dummy Activity"
    assert activity.duration == datetime.timedelta(minutes=31)
    assert activity.distance == 2.3
    assert activity.description == "super sport"
    assert activity.sport.name == "Cycling"
    assert activity.date.date() == timezone.now().date()


def test_activity_page__rendering_of_sport_icon_on_map(insert_sport, import_one_activity, live_server, webdriver):
    # Test to fix issue GH72
    icon_name = "running"
    insert_sport(name="Jogging", icon=icon_name)

    import_one_activity("2019-09-18-16-02-35.fit")
    activity = models.Activity.objects.get()
    pk = activity.pk

    # check out laps of activity
    assert models.Lap.objects.count() == 2
    laps = models.Lap.objects.all()
    for lap in laps:
        # each lap has at least one coordinate
        assert lap.end_lat is not None or lap.start_lat is not None
        assert lap.end_long is not None or lap.start_long is not None
        assert lap.trigger != "manual"

    # insert an additional lap without any coordinate data
    lap_obj = models.Lap(
        start_time=activity.date,
        end_time=activity.date + datetime.timedelta(minutes=1),
        elapsed_time=-datetime.timedelta(minutes=100),
        trigger="manual",
        start_lat=None,
        start_long=None,
        end_lat=None,
        end_long=None,
        distance=1.0,
        speed=10.0,
        trace=activity.trace_file,
    )
    lap_obj.save()
    assert models.Lap.objects.count() == 3
    new_lap = models.Lap.objects.get(trigger="manual")
    assert new_lap.start_lat is None
    assert new_lap.start_long is None
    assert new_lap.end_lat is None
    assert new_lap.end_long is None

    webdriver.get(live_server.url + f"/activity/{pk}")
    assert webdriver.current_url == f"{live_server.url}/activity/{pk}"

    initial_number_of_sport_icons = len(webdriver.find_elements_by_class_name(f"fa-{icon_name}"))

    headings = [h.text for h in webdriver.find_elements_by_tag_name("h5")]
    assert "Fastest Sections  " in headings
    assert "Speed" in headings
    assert "Pace" in headings
    assert "Temperature" in headings
    assert "Laps" in headings

    # hover over ploy in order to trigger the rendering of the sport icon on the leaflet map
    altitude_plot = webdriver.find_element(By.CSS_SELECTOR, ".bk:nth-child(1) > .bk > .bk-canvas-events")
    altitude_plot.click()
    hover = ActionChains(webdriver).move_to_element(altitude_plot)
    hover.perform()

    # once sport icon gets rendered we should find one more sport icons than before
    number_of_sport_icons_when_hovering = len(webdriver.find_elements_by_class_name(f"fa-{icon_name}"))
    assert number_of_sport_icons_when_hovering == initial_number_of_sport_icons + 1
