#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import time
import unittest

import pbtest

from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait


def get_domain_slider_state(driver, domain):
    label = driver.find_element_by_css_selector(
        'input[name="{}"][checked]'.format(domain))
    return label.get_attribute('value')


class PopupTest(pbtest.PBSeleniumTest):
    """Make sure the popup works correctly."""

    def clear_seed_data(self):
        self.load_url(self.options_url)
        self.js("chrome.extension.getBackgroundPage().badger.storage.clearTrackerData();")

    def wait_for_page_to_start_loading(self, url, timeout=20):
        """Wait until the title element is present. Use it to work around
        Firefox not updating self.driver.current_url fast enough."""
        try:
            WebDriverWait(self.driver, timeout).until(
                expected_conditions.presence_of_element_located(
                    (By.CSS_SELECTOR, "title")))
        except TimeoutException:
            # TODO debug info
            print("\n")
            print("driver.current_url=" + self.driver.current_url)
            print()
            print(self.driver.page_source[:5000])
            print("...\n")

            self.fail("Timed out waiting for %s to start loading" % url)

    def open_popup(self, show_nag=False, origins=None):
        """Open popup and optionally close overlay."""

        DUMMY_PAGE_URL = "https://efforg.github.io/privacybadger-test-fixtures/"

        # hack to get tabData populated for the popup's tab
        # to get the popup shown for regular pages
        # as opposed to special (no-tabData) browser pages
        self.open_window()
        self.load_url(DUMMY_PAGE_URL)

        self.open_window()
        self.load_url(self.popup_url)
        self.wait_for_script("return window.POPUP_INITIALIZED")

        # override tab ID (to get regular page popup instead of
        # special browser page popup),
        # optionally set the domains the popup should report,
        # optionally ask for the new user welcome page reminder
        popup_js = (
            "(function (origins, show_nag, DUMMY_PAGE_URL) {"
            "chrome.tabs.query({ url: DUMMY_PAGE_URL }, (tabs) => {"
            "  chrome.runtime.sendMessage({"
            "    type: 'getPopupData',"
            "    tabId: tabs[0].id"
            "  }, (response) => {"
            "    response.seenComic = !show_nag;"
            "    response.origins = origins;"
            "    setPopupData(response);"
            "    refreshPopup();"
            "    showNagMaybe();"
            "    window.DONE_REFRESHING = true;"
            "  });"
            "});"
            "}(arguments[0], arguments[1], arguments[2]));"
        )
        self.js(popup_js, origins if origins else {}, show_nag, DUMMY_PAGE_URL)
        # wait until the async getTab function is done
        self.wait_for_script(
            "return typeof window.DONE_REFRESHING != 'undefined'",
            timeout=5,
            message="Timed out waiting for getTab() to complete."
        )

        # wait for any sliders to finish rendering
        self.wait_for_script("return window.SLIDERS_DONE")

    def get_enable_button(self):
        """Get enable button on popup."""
        return self.driver.find_element_by_id("activate_site_btn")

    def get_disable_button(self):
        """Get disable button on popup."""
        return self.driver.find_element_by_id("deactivate_site_btn")

    def test_welcome_page_reminder_overlay(self):
        """Ensure overlay links to new user welcome page."""

        # first close the welcome page if already open
        try:
            self.close_window_with_url(self.first_run_url, max_tries=1)
        except pbtest.WindowNotFoundException:
            pass

        self.open_popup(show_nag=True)
        self.driver.find_element_by_id("intro-reminder-btn").click()

        # Look for first run page and return if found.
        self.switch_to_window_with_url(self.first_run_url)

    def test_help_button(self):
        """Ensure FAQ website is opened when help button is clicked."""

        FAQ_URL = "https://privacybadger.org/#faq"

        try:
            self.switch_to_window_with_url(FAQ_URL, max_tries=1)
        except pbtest.WindowNotFoundException:
            pass
        else:
            self.fail("FAQ should not already be open")

        self.open_popup()
        self.driver.find_element_by_id("help").click()

        self.switch_to_window_with_url(FAQ_URL)

    def test_options_button(self):
        """Ensure options page is opened when button is clicked."""
        self.open_popup()
        self.driver.find_element_by_id("options").click()
        self.switch_to_window_with_url(self.options_url)

    @pbtest.repeat_if_failed(5)
    def test_trackers_link(self):
        """Ensure trackers link opens EFF website."""

        EFF_URL = "https://privacybadger.org/#What-is-a-third-party-tracker"

        self.open_popup()

        # Get all possible tracker links (none, one, multiple)
        trackers_links = self.driver.find_elements_by_css_selector("#pbInstructions a")
        if not trackers_links:
            self.fail("Unable to find trackers link on popup")

        # Get the one that's displayed on the page that this test is using
        for link in trackers_links:
            if link.is_displayed():
                trackers_link = link

        trackers_link.click()

        # Make sure EFF website not opened in same window.
        if self.driver.current_url != self.popup_url:
            self.fail("EFF website not opened in new window")

        # Look for EFF website and return if found.
        self.switch_to_window_with_url(EFF_URL)

        self.wait_for_page_to_start_loading(EFF_URL)

        self.assertEqual(self.driver.current_url, EFF_URL,
            "EFF website should open after clicking trackers link on popup")

        # Verify EFF website contains the linked anchor element.
        faq_selector = 'a[href="{}"]'.format(EFF_URL[EFF_URL.index('#'):])
        try:
            WebDriverWait(self.driver, pbtest.SEL_DEFAULT_WAIT_TIMEOUT).until(
                expected_conditions.presence_of_element_located(
                    (By.CSS_SELECTOR, faq_selector)))
        except TimeoutException:
            self.fail("Unable to find expected element ({}) on EFF website".format(faq_selector))

    def test_toggling_sliders(self):
        """Ensure toggling sliders is persisted."""
        self.clear_seed_data()

        # enable learning to show not-yet-blocked domains in popup
        self.wait_for_script("return window.OPTIONS_INITIALIZED")
        self.find_el_by_css('#local-learning-checkbox').click()

        DOMAIN = "example.com"
        DOMAIN_ID = DOMAIN.replace(".", "-")

        self.open_popup(origins={DOMAIN:"allow"})

        # click input with JavaScript to avoid "Element ... is not clickable" /
        # "Other element would receive the click" Selenium limitation
        self.js("$('#block-{}').click()".format(DOMAIN_ID))

        # retrieve the new action
        self.load_url(self.options_url)
        self.wait_for_script("return window.OPTIONS_INITIALIZED")
        self.find_el_by_css('a[href="#tab-tracking-domains"]').click()
        new_action = get_domain_slider_state(self.driver, DOMAIN)

        self.assertEqual(new_action, "block",
            "The domain should be blocked on options page.")

        # test toggling some more
        self.open_popup(origins={DOMAIN:"user_block"})

        self.assertTrue(
            self.driver.find_element_by_id("block-" + DOMAIN_ID).is_selected(),
            "The domain should be shown as blocked in popup."
        )

        # change to "cookieblock"
        self.js("$('#cookieblock-{}').click()".format(DOMAIN_ID))
        # change again to "block"
        self.js("$('#block-{}').click()".format(DOMAIN_ID))

        # retrieve the new action
        self.load_url(self.options_url)
        self.wait_for_script("return window.OPTIONS_INITIALIZED")
        self.find_el_by_css('a[href="#tab-tracking-domains"]').click()
        new_action = get_domain_slider_state(self.driver, DOMAIN)

        self.assertEqual(new_action, "block",
            "The domain should still be blocked on options page.")

    def test_reverting_control(self):
        """Test restoring control of a domain to Privacy Badger."""
        self.clear_seed_data()

        DOMAIN = "example.com"
        DOMAIN_ID = DOMAIN.replace(".", "-")

        # record the domain as cookieblocked by Badger
        self.cookieblock_domain(DOMAIN)

        self.open_popup(origins={DOMAIN:"cookieblock"})

        # set the domain to user control
        # click input with JavaScript to avoid "Element ... is not clickable" /
        # "Other element would receive the click" Selenium limitation
        self.js("$('#block-{}').click()".format(DOMAIN_ID))

        # restore control to Badger
        self.driver.find_element_by_css_selector(
            'div[data-origin="{}"] a.honeybadgerPowered'.format(DOMAIN)
        ).click()

        # get back to a valid window handle as the window just got closed
        self.driver.switch_to.window(self.driver.window_handles[0])

        # verify the domain is no longer user controlled
        self.load_url(self.options_url)
        self.wait_for_script("return window.OPTIONS_INITIALIZED")
        self.find_el_by_css('a[href="#tab-tracking-domains"]').click()

        # assert the action is not what we manually clicked
        action = get_domain_slider_state(self.driver, DOMAIN)
        self.assertEqual(action, "cookieblock",
            "Domain's action should have been restored.")

        # assert the undo arrow is not displayed
        self.driver.find_element_by_css_selector('a[href="#tab-tracking-domains"]').click()
        self.driver.find_element_by_id('show-tracking-domains-checkbox').click()
        self.assertFalse(
            self.driver.find_element_by_css_selector(
                'div[data-origin="{}"] a.honeybadgerPowered'.format(DOMAIN)
            ).is_displayed(),
            "Undo arrow should not be displayed."
        )

    def test_disable_enable_buttons(self):
        """Ensure disable/enable buttons change popup state."""

        DISPLAYED_ERROR = " should not be displayed on popup"
        NOT_DISPLAYED_ERROR = " should be displayed on popup"

        self.open_popup()

        self.get_disable_button().click()

        # get back to a valid window handle as the window just got closed
        self.driver.switch_to.window(self.driver.window_handles[0])
        self.open_popup()

        # Check that popup state changed after disabling.
        disable_button = self.get_disable_button()
        self.assertFalse(disable_button.is_displayed(),
                         "Disable button" + DISPLAYED_ERROR)
        enable_button = self.get_enable_button()
        self.assertTrue(enable_button.is_displayed(),
                        "Enable button" + NOT_DISPLAYED_ERROR)

        enable_button.click()

        self.driver.switch_to.window(self.driver.window_handles[0])
        self.open_popup()

        # Check that popup state changed after re-enabling.
        disable_button = self.get_disable_button()
        self.assertTrue(disable_button.is_displayed(),
                        "Disable button" + NOT_DISPLAYED_ERROR)
        enable_button = self.get_enable_button()
        self.assertFalse(enable_button.is_displayed(),
                         "Enable button" + DISPLAYED_ERROR)

    def test_error_button(self):
        """Ensure error button opens report error overlay."""
        self.open_popup()

        # TODO: selenium firefox has a bug where is_displayed() is always True
        # for these elements. But we should use is_displayed when this is fixed.
        #overlay_input = self.driver.find_element_by_id("error_input")
        #self.assertTrue(overlay_input.is_displayed(), "User input" + error_message)

        # assert error reporting menu is not open
        self.assertTrue(len(self.driver.find_elements_by_class_name('active')) == 0,
                'error reporting should not be open')

        # Click error button to open overlay for reporting sites.
        error_button = self.driver.find_element_by_id("error")
        error_button.click()
        time.sleep(1)

        # check error is open
        self.assertTrue(len(self.driver.find_elements_by_class_name('active')) == 1,
                'error reporting should be open')

        overlay_close = self.driver.find_element_by_id("report_close")
        overlay_close.click()
        time.sleep(1)
        self.assertTrue(len(self.driver.find_elements_by_class_name('active')) == 0,
                'error reporting should be closed again')

    @pbtest.repeat_if_failed(5)
    def test_donate_button(self):
        """Ensure donate button opens EFF website."""

        EFF_URL = "https://supporters.eff.org/donate/support-privacy-badger"

        self.open_popup()

        donate_button = self.driver.find_element_by_id("donate")

        donate_button.click()

        # Make sure EFF website not opened in same window.
        if self.driver.current_url != self.popup_url:
            self.fail("EFF website not opened in new window")

        # Look for EFF website and return if found.
        self.switch_to_window_with_url(EFF_URL)

        self.wait_for_page_to_start_loading(EFF_URL)

        self.assertEqual(self.driver.current_url, EFF_URL,
            "EFF website should open after clicking donate button on popup")


if __name__ == "__main__":
    unittest.main()
