import unittest
import uuid

from mockito import mock

from SeleniumLibrary.errors import WindowNotFound
from SeleniumLibrary.locators import WindowManager


class WindowManagerTests(unittest.TestCase):

    def test_select_with_invalid_prefix(self):
        manager = WindowManagerWithMockBrowser()
        with self.assertRaises(WindowNotFound) as context:
            manager.select("something=test1")
        self.assertEqual(
            str(context.exception),
            "No window matching handle, name, title or URL 'something=test1' found."
        )

    def test_select_by_title(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        manager.select("title=Title 2")
        self.assertEqual(manager.driver.current_window.name, 'win2')

    def test_select_by_title_with_multiple_matches(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2a', 'title': "Title 2", 'url': 'http://localhost/page2a.html'},
            {'name': 'win2b', 'title': "Title 2", 'url': 'http://localhost/page2b.html'}
        )
        manager.select("title=Title 2")
        self.assertEqual(manager.driver.current_window.name, 'win2a')

    def test_select_by_title_no_match(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        with self.assertRaises(WindowNotFound) as context:
            manager.select("title=Title -1")
        self.assertEqual(str(context.exception),
                         "Unable to locate window with title 'Title -1'.")

    def test_select_by_name(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        manager.select("name=win2")
        self.assertEqual(manager.driver.current_window.name, 'win2')

    def test_select_by_name_no_match(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        with self.assertRaises(WindowNotFound) as context:
            manager.select("name=win-1")
        self.assertEqual(str(context.exception),
                         "Unable to locate window with name 'win-1'.")

    def test_select_by_url(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        manager.select("url=http://localhost/page2.html")
        self.assertEqual(manager.driver.current_window.name, 'win2')

    def test_select_by_url_with_multiple_matches(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2a', 'title': "Title 2a", 'url': 'http://localhost/page2.html'},
            {'name': 'win2b', 'title': "Title 2b", 'url': 'http://localhost/page2.html'}
        )
        manager.select("url=http://localhost/page2.html")
        self.assertEqual(manager.driver.current_window.name, 'win2a')

    def test_select_by_url_no_match(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        with self.assertRaises(WindowNotFound) as context:
            manager.select("url=http://localhost/page-1.html")
        self.assertEqual(
            str(context.exception),
            "Unable to locate window with URL 'http://localhost/page-1.html'."
        )

    def test_select_main_window(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        manager.select("name=win2")
        self.assertEqual(manager.driver.current_window.name, 'win2')
        manager.select("main")
        self.assertEqual(manager.driver.current_window.name, 'win1')
        manager.select("MAIN")
        self.assertEqual(manager.driver.current_window.name, 'win1')

    def test_select_by_default_with_name(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        manager.select("win2")
        self.assertEqual(manager.driver.current_window.name, 'win2')

    def test_select_by_default_with_title(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        manager.select("Title 2")
        self.assertEqual(manager.driver.current_window.name, 'win2')

    def test_select_by_default_no_match(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        with self.assertRaises(WindowNotFound) as context:
            manager.select('foobar')
        self.assertEqual(
            str(context.exception),
            "No window matching handle, name, title or URL 'foobar' found."
        )

    def test_prefix_is_case_sensitive(self):
        manager = WindowManagerWithMockBrowser(
            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
        )
        manager.select("name=win2")
        self.assertEqual(manager.driver.current_window.name, 'win2')
        with self.assertRaises(WindowNotFound) as context:
            manager.select("nAmE=win2")
        self.assertEqual(
            str(context.exception),
            "No window matching handle, name, title or URL 'nAmE=win2' found."
        )

    def test_get_window_infos(self):
        manager = WindowManagerWithMockBrowser(
            {'id': 'id1', 'name': 'win1', 'title': "Title 1", 'url': 'http://url.1'},
            {'id': 'id2', 'name': 'win2', 'title': "Title 2", 'url': 'http://url.2'},
            {'name': 'win3', 'title': "Title 3", 'url': 'http://url.3'}
        )
        self.assertEqual([info.id for info in manager.get_window_infos()],
                         ['id1', 'id2', 'undefined'])
        self.assertEqual([info.name for info in manager.get_window_infos()],
                         ['win1', 'win2', 'win3'])
        self.assertEqual([info.title for info in manager.get_window_infos()],
                         ['Title 1', 'Title 2', 'Title 3'])
        self.assertEqual([info.url for info in manager.get_window_infos()],
                         ['http://url.1', 'http://url.2', 'http://url.3'])


class WindowManagerWithMockBrowser(WindowManager):

    def __init__(self, *window_specs):
        ctx = mock()
        ctx.driver = self._make_mock_driver(*window_specs)
        WindowManager.__init__(self, ctx)

    def _make_mock_driver(self, *window_specs):
        driver = mock()
        current_window = mock()
        driver.window_handles = []
        window_infos = {}
        for window_spec in window_specs:
            handle = uuid.uuid4().hex
            driver.window_handles.append(handle)
            id_ = window_spec.get('id')
            if not id_:
                id_ = 'undefined'
            window_info = [
                id_,
                window_spec.get('name'),
                window_spec.get('title'),
                window_spec.get('url')
            ]
            window_infos[handle] = window_info

        def window(handle_):
            if handle_ in driver.window_handles:
                driver.session_id = handle_
                current_window.name = window_infos[handle_][1]
                driver.current_window = current_window
                driver.title = window_infos[handle_][2]
                driver.current_url = window_infos[handle_][3]

        switch_to = mock()
        switch_to.window = window
        driver.switch_to = switch_to

        def execute_script(script):
            handle_ = driver.session_id
            if handle_ in driver.window_handles:
                return window_infos[handle_][:2]

        driver.execute_script = execute_script
        return driver
