import time
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium.webdriver.firefox.webdriver import WebDriver

from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.expected_conditions import presence_of_element_located
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException

from django.conf import settings
from django.utils import timezone
from datetime import timedelta
from lambdainst.models import User
from payments.models import Payment, Subscription


class BaseOnlineTest(StaticLiveServerTestCase):
    # using a fixed port because you're supposed to forward a public ip to it
    # for the web hooks
    # $ ssh relaybox -R 12345:127.0.0.1:8000
    # (set ROOT_URL to the right public url and RUN_ONLINE_TESTS to True)
    port = 8000

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.selenium = WebDriver(firefox_binary="/usr/bin/firefox")
        cls.selenium.implicitly_wait(12)

        cls.root_url = settings.ROOT_URL
        cls.wait = WebDriverWait(cls.selenium, 20)

    @classmethod
    def tearDownClass(cls):
        cls.selenium.quit()
        super().tearDownClass()

    def _signup(self):
        self.selenium.get("%s%s" % (self.root_url, "/account/signup"))
        username_input = self.selenium.find_element_by_name("username")
        username_input.send_keys("test-user")
        password_input = self.selenium.find_element_by_name("password")
        password_input.send_keys("test-password")
        password_input = self.selenium.find_element_by_name("password2")
        password_input.send_keys("test-password")
        self.selenium.find_element_by_xpath('//input[@value="Sign up"]').click()


def wait(f, initial=3, between=2):
    time.sleep(initial)
    print("waiting for confirmation...")
    while True:
        try:
            f()
            break
        except (NoSuchElementException, AssertionError) as exc:
            print(f"confirmation text not found ({exc!r})")
        time.sleep(between)


class OnlineStripeTests(BaseOnlineTest):
    fixtures = []

    def test_payment(self):
        self._signup()

        self.selenium.find_element_by_xpath('//label[@for="tab_onetime"]').click()
        self.wait.until(
            EC.visibility_of(
                self.selenium.find_element_by_xpath(
                    '//label[@for="tab_onetime"]/..//select[@name="payment-method"]'
                )
            )
        )
        self.selenium.find_element_by_xpath(
            '//label[@for="tab_onetime"]/..//select[@name="plan"]/option[@value="3m"]'
        ).click()
        self.selenium.find_element_by_xpath(
            '//label[@for="tab_onetime"]/..//select[@name="payment-method"]/option[@value="stripe"]'
        ).click()
        self.selenium.find_element_by_xpath(
            '//label[@for="tab_onetime"]/..//input[@value="Buy Now"]'
        ).click()

        assert self.selenium.find_element_by_xpath('//span[text()="Test Mode"]')

        self.selenium.find_element_by_xpath('//input[@name="email"]').send_keys(
            "test@ccrypto.org"
        )
        self.selenium.find_element_by_xpath('//input[@name="cardNumber"]').send_keys(
            "4242424242424242"
        )
        self.selenium.find_element_by_xpath('//input[@name="cardExpiry"]').send_keys(
            "6/66"
        )
        self.selenium.find_element_by_xpath('//input[@name="cardCvc"]').send_keys("420")
        self.selenium.find_element_by_xpath('//input[@name="billingName"]').send_keys(
            "Test User"
        )
        self.selenium.find_element_by_xpath('//button[@type="submit"]').click()

        self.wait.until(
            EC.presence_of_element_located((By.XPATH, '//*[contains(text(), "Waiting for payment")]'))
        )

        def check_active():
            # refresh payment as we dont have a worker
            p = Payment.objects.order_by('id').first()
            p.refresh()

            self.selenium.refresh()
            self.selenium.find_element_by_xpath('//h2[contains(text(),"Confirmed")]')

        wait(check_active)

        user = User.objects.get(username="test-user")
        assert user.vpnuser.is_paid
        assert user.vpnuser.expiration >= (timezone.now() + timedelta(days=89))

    def test_subscription(self):
        self._signup()

        self.selenium.find_element_by_xpath('//label[@for="tab_subscr"]').click()
        self.wait.until(
            EC.visibility_of(
                self.selenium.find_element_by_xpath(
                    '//label[@for="tab_subscr"]/..//select[@name="payment-method"]'
                )
            )
        )
        self.selenium.find_element_by_xpath(
            '//label[@for="tab_subscr"]/..//select[@name="plan"]/option[@value="12m"]'
        ).click()
        self.selenium.find_element_by_xpath(
            '//label[@for="tab_subscr"]/..//select[@name="payment-method"]/option[@value="stripe"]'
        ).click()
        self.selenium.find_element_by_xpath(
            '//label[@for="tab_subscr"]/..//input[@value="Subscribe"]'
        ).click()

        assert self.selenium.find_element_by_xpath('//span[text()="Test Mode"]')

        self.selenium.find_element_by_xpath('//input[@name="email"]').send_keys(
            "test@ccrypto.org"
        )
        self.selenium.find_element_by_xpath('//input[@name="cardNumber"]').send_keys(
            "4242424242424242"
        )
        self.selenium.find_element_by_xpath('//input[@name="cardExpiry"]').send_keys(
            "6/66"
        )
        self.selenium.find_element_by_xpath('//input[@name="cardCvc"]').send_keys("420")
        self.selenium.find_element_by_xpath('//input[@name="billingName"]').send_keys(
            "Test User"
        )
        self.selenium.find_element_by_xpath('//button[@type="submit"]').click()

        self.wait.until(
            EC.presence_of_element_located((By.XPATH, '//*[contains(text(), "Your subscription is processing.")]'))
        )

        def check_active():
            # refresh sub as we dont have a worker
            p = Subscription.objects.order_by('id').first()
            p.refresh()

            self.selenium.refresh()
            sub_status = self.selenium.find_element_by_xpath(
                '//td[text()="Subscription"]//following-sibling::td'
            )
            assert sub_status.text.startswith("ACTIVE")

        wait(check_active)

        sub_status = self.selenium.find_element_by_xpath(
            '//td[text()="Subscription"]//following-sibling::td'
        )
        assert sub_status.text.startswith("ACTIVE")

        user = User.objects.get(username="test-user")
        assert user.vpnuser.is_paid

        sub_status.find_element_by_xpath('a[text()="cancel"]').click()
        self.selenium.find_element_by_xpath(
            '//input[@value="Cancel Subscription"]'
        ).click()

        sub_status = self.selenium.find_element_by_xpath(
            '//td[text()="Subscription"]//following-sibling::td'
        )
        assert not sub_status.text.startswith("ACTIVE")

        user = User.objects.get(username="test-user")
        assert user.vpnuser.is_paid
        assert not user.vpnuser.get_subscription()
