#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime
import logging
from sys import _getframe
import random
import re
import urllib.parse

from django.contrib.auth.models import User
from django.db.models import Q

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait
from selenose.cases import SeleniumTestCase

from vm.models import Instance
from .config import SeleniumConfig
from .util import CircleSeleniumMixin

conf = SeleniumConfig()
log_formatter = logging.Formatter(conf.log_format)
logger = logging.getLogger(conf.logger_name)
fileHandler = logging.handlers.RotatingFileHandler(
    conf.log_file, maxBytes=conf.log_size, backupCount=conf.log_backup)
fileHandler.setFormatter(log_formatter)
fileHandler.setLevel(logging.WARNING)
logger.addHandler(fileHandler)


class BasicSeleniumTests(SeleniumTestCase, CircleSeleniumMixin):
    template_ids = []
    vm_ids = []

    def __init__(self, *args, **kwargs):
        super(self.__class__, self).__init__(*args, **kwargs)
        self.conf = conf

    @classmethod
    def setup_class(cls):
        logger.warning("Selenium test started @ %(time)s" % {
            'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
        if conf.create_user:
            logger.warning(
                "Creating selenium test user %(name)s:%(password)s" % {
                    'name': conf.client_name,
                    'password': conf.random_pass})
            cls._user = User.objects.create(username=conf.client_name,
                                            is_superuser=True)
            cls._user.set_password(conf.random_pass)
            cls._user.save()

    @classmethod
    def teardown_class(cls):
        if conf.create_user:
            for instance in Instance.objects.all().filter(
                    ~Q(status='DESTROYED'), owner=cls._user):
                logger.warning(
                    "Destroying the test virtual machine: %(id)s" % {
                        'id': instance.pk})
                instance.destroy(system=True)
            logger.warning("Deleting test user %(name)s" % {
                'name': conf.client_name})
            cls._user.delete()
        logger.warning("Selenium test finished @ %(time)s" % {
            'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')})

    def test_01_login(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        title = 'Dashboard | CIRCLE'
        location = '/dashboard/'
        self.login()
        self.driver.get('%s/dashboard/' % conf.host)
        url = urllib.parse.urlparse(self.driver.current_url)
        (self.assertIn('%s' % title, self.driver.title,
                       '%s is not found in the title' % title) or
            self.assertEqual(url.path, '%s' % location,
                             'URL path is not equal with %s' % location))

    def test_02_add_template_rights(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        template_pool = self.get_template_id(from_all=True)
        if len(template_pool) > 1:
            chosen = template_pool[random.randint(0, len(template_pool) - 1)]
        elif len(template_pool) == 1:
            chosen = template_pool[0]
        else:
            logger.exception("Selenium did not found any templates")
            raise Exception(
                "Selenium did not found any templates")
        self.driver.get('%s/dashboard/template/%s/' % (conf.host, chosen))
        acces_form_css = (
            "form[action*='/dashboard/template/%(template_id)s/acl/']"
            "[method='post']" % {
                'template_id': chosen})
        acces_form = self.driver.find_element_by_css_selector(acces_form_css)
        user_name = acces_form.find_element_by_css_selector(
            "input[type='text'][id='id_name']")
        user_status = acces_form.find_element_by_css_selector(
            "select[name='level']")
        user_name.clear()
        user_name.send_keys(conf.client_name)
        self.select_option(user_status, 'user')
        # For strange reasons clicking on submit button doesn't work anymore
        acces_form.submit()
        found_users = []
        acces_form = self.driver.find_element_by_css_selector(acces_form_css)
        acl_users = acces_form.find_elements_by_css_selector(
            "a[href*='/dashboard/profile/']")
        for user in acl_users:
            user_text = re.split(r':[ ]?', user.text)
            if len(user_text) == 2:
                found_name = re.search(r'[\w\W]+(?=\))', user_text[1]).group()
                logger.warning("'%(user)s' found in ACL "
                               "list for template %(id)s" % {
                                   'user': found_name,
                                   'id': chosen})
                found_users.append(found_name)
        self.assertIn(conf.client_name, found_users,
                      "Could not add user to template's ACL")

    def test_03_able_to_create_template(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        template_list = None
        create_template = self.get_link_by_href('/dashboard/template/choose/')
        self.click_on_link(create_template)
        WebDriverWait(self.driver, conf.wait_max_sec).until(
            ec.visibility_of_element_located((
                By.ID, 'confirmation-modal')))
        template_list = self.driver.find_elements_by_class_name(
            'template-choose-list-element')
        logger.warning('Selenium found %(count)s template possibilities' % {
            'count': len(template_list)})
        (self.assertIsNotNone(
            template_list, "Selenium can not find the create template list") or
            self.assertGreater(len(template_list), 0,
                               "The create template list is empty"))

    def test_04_create_base_template(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        created_template_id = self.get_template_id(
            self.create_base_template())
        found = created_template_id is not None
        if found:
            self.template_ids.extend(created_template_id)
        self.assertTrue(
            found,
            "Could not found the created template in the template list")

    def test_05_create_template_from_base(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        created_template_id = self.get_template_id(
            self.create_template_from_base())
        found = created_template_id is not None
        if found:
            self.template_ids.extend(created_template_id)
        self.assertTrue(
            found,
            "Could not found the created template in the template list")

    def test_06_delete_templates(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        success = False
        self.login()
        for template_id in self.template_ids:
            logger.warning("Deleting template %s" % template_id)
            self.delete_template(template_id)
        existing_templates = self.get_template_id()
        if len(existing_templates) == 0:
            success = True
        else:
            for template_id in self.template_ids:
                if template_id not in existing_templates:
                    self.template_ids.remove(template_id)
            if len(self.template_ids) == 0:
                success = True
        self.assertTrue(
            success, "Could not delete (all) the test template(s)")

    def test_07_able_to_create_vm(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        vm_list = None
        create_vm_link = self.get_link_by_href('/dashboard/vm/create/')
        create_vm_link.click()
        WebDriverWait(self.driver, conf.wait_max_sec).until(
            ec.visibility_of_element_located((
                By.ID, 'confirmation-modal')))
        vm_list = self.driver.find_elements_by_class_name(
            'vm-create-template-summary')
        logger.warning("Selenium found %(vm_number)s virtual machine"
                       " template possibilities" % {
                           'vm_number': len(vm_list)})
        (self.assertIsNotNone(
            vm_list, "Selenium can not find the VM list") or
            self.assertGreater(len(vm_list), 0, "The create VM list is empty"))

    def test_08_create_vm(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        pk = self.create_random_vm()
        self.vm_ids.append(pk)
        self.assertIsNotNone(pk, "Can not create a VM")

    def test_09_vm_view_change(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        expected_states = ["", "none",
                           "none", "",
                           "block", "none"]
        states = self.view_change("vm")
        logger.warning('states: [%s]' % ', '.join(map(str, states)))
        logger.warning('expected: [%s]' % ', '.join(map(str, expected_states)))
        self.assertListEqual(states, expected_states,
                             "The view mode does not change for VM listing")

    def test_10_node_view_change(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        expected_states = ["", "none",
                           "none", "",
                           "block", "none"]
        states = self.view_change("node")
        logger.warning('states: [%s]' % ', '.join(map(str, states)))
        logger.warning('expected: [%s]' % ', '.join(map(str, expected_states)))
        self.assertListEqual(states, expected_states,
                             "The view mode does not change for NODE listing")

    def test_11_delete_vm(self):
        logger.warning("Starting test %s" % _getframe().f_code.co_name)
        self.login()
        succes = True
        for vm in self.vm_ids:
            if not self.delete_vm(vm):
                succes = False
            else:
                self.vm_ids.remove(vm)
        self.assertTrue(succes, "Can not delete all VM")
