#!/usr/bin/env python3

# SPDX-License-Identifier: BSD-3-Clause
# SPDX-FileCopyrightText: 2022-2023 Harald Sitter <sitter@kde.org>

import os
import subprocess
import sys
import time
import unittest
from typing import Final

from appium import webdriver
from appium.options.common.base import AppiumOptions
from appium.webdriver.common.appiumby import AppiumBy
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait

sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir, "desktop"))
from desktoptest import name_has_owner, start_kactivitymanagerd

WIDGET_ID: Final = "org.kde.plasma.kickoff"
KDE_VERSION: Final = 6


class KickoffTests(unittest.TestCase):
    driver: webdriver.Remote
    kactivitymanagerd: subprocess.Popen | None = None

    @classmethod
    def setUpClass(cls) -> None:
        cls.kactivitymanagerd = start_kactivitymanagerd()

        options = AppiumOptions()
        options.set_capability("environ", {
            "LC_ALL": "en_US.UTF-8",
        })
        options.set_capability("app", f"plasmawindowed -p org.kde.plasma.desktop {WIDGET_ID}")
        options.set_capability("timeouts", {'implicit': 10000})
        cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', options=options)

    @classmethod
    def tearDownClass(self) -> None:
        subprocess.check_call([f"kquitapp{KDE_VERSION}", "plasmawindowed"])
        self.driver.quit()
        if self.kactivitymanagerd is not None:
            self.kactivitymanagerd.terminate()
            self.kactivitymanagerd.wait()

    def setUp(self) -> None:
        ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
        self.driver.find_element(by=AppiumBy.CLASS_NAME, value="[button | Application Launcher]").click()
        self.driver.find_element(by=AppiumBy.CLASS_NAME, value="[list item | All Applications]")

    def tearDown(self) -> None:
        """
        Take screenshot when the current test fails
        """
        if not self._outcome.result.wasSuccessful():
            self.driver.get_screenshot_as_file(f"failed_test_shot_{WIDGET_ID}_#{self.id()}.png")
            print(self.driver.page_source, file=sys.stderr)

    def test_1_search_calculator(self) -> None:
        search_field = self.driver.find_element(by=AppiumBy.NAME, value="Search")
        search_field.send_keys("1")
        search_field.send_keys("2345+67890")
        self.driver.find_element(by=AppiumBy.CLASS_NAME, value="[list item | 80235]")

    def test_2_search_app(self) -> None:
        # Emoji Selector is the only actual application we install from workspace :|
        self.driver.find_element(by=AppiumBy.NAME, value="Search").send_keys("Emoji Selector")
        self.driver.find_element(by=AppiumBy.CLASS_NAME, value="[list item | Emoji Selector]").click()
        WebDriverWait(self.driver, 5).until(lambda _: name_has_owner(None, "org.kde.plasma.emojier"))
        subprocess.check_call([f"kquitapp{KDE_VERSION}", "plasma.emojier"])

    def test_3_keyboard_navigation(self) -> None:
        focused_elements = self.driver.find_elements(by=AppiumBy.XPATH, value="//table_cell[contains(@states, 'focused')]")
        self.assertEqual(len(focused_elements), 1)
        first_favorite = focused_elements[0].id
        self.assertIn("KickoffGridDelegate", focused_elements[0].get_attribute('accessibility-id'))

        # Go right to second favorite
        ActionChains(self.driver).send_keys(Keys.RIGHT).perform()
        focused_elements = self.driver.find_elements(by=AppiumBy.XPATH, value="//table_cell[contains(@states, 'focused')]")
        self.assertEqual(len(focused_elements), 1)
        self.assertNotEqual(first_favorite, focused_elements[0].id)

        # Go left to first favorite again
        ActionChains(self.driver).send_keys(Keys.LEFT).perform()
        focused_elements = self.driver.find_elements(by=AppiumBy.XPATH, value="//table_cell[contains(@states, 'focused')]")
        self.assertEqual(len(focused_elements), 1)
        self.assertEqual(first_favorite, focused_elements[0].id)

        # Go further left to the category list
        ActionChains(self.driver).key_down(Keys.SHIFT).send_keys(Keys.TAB).key_up(Keys.ALT).perform()
        focused_elements = self.driver.find_elements(by=AppiumBy.XPATH, value="//list_item[contains(@states, 'focused')]")
        self.assertEqual(len(focused_elements), 1)
        self.assertNotEqual(first_favorite, focused_elements[0].id)
        favorites_category = focused_elements[0].id

        # Go down to the 'all apps' category
        ActionChains(self.driver).send_keys(Keys.DOWN).perform()
        focused_elements = self.driver.find_elements(by=AppiumBy.XPATH, value="//list_item[contains(@states, 'focused')]")
        self.assertEqual(len(focused_elements), 1)
        self.assertNotEqual(favorites_category, focused_elements[0].id)

        # Go right to the first all app. This must not be a grid delegate anymore (favorites are griddelegates)
        ActionChains(self.driver).send_keys(Keys.RIGHT).perform()
        focused_elements = self.driver.find_elements(by=AppiumBy.XPATH, value="//list_item[contains(@states, 'focused')]")
        self.assertEqual(len(focused_elements), 1)
        self.assertNotEqual(favorites_category, focused_elements[0].id)
        self.assertNotEqual(first_favorite, focused_elements[0].id)
        self.assertNotIn("KickoffGridDelegate", focused_elements[0].get_attribute('accessibility-id'))
        first_all_app = focused_elements[0].id

        # Go down to second all app
        ActionChains(self.driver).send_keys(Keys.DOWN).perform()
        focused_elements = self.driver.find_elements(by=AppiumBy.XPATH, value="//list_item[contains(@states, 'focused')]")
        self.assertEqual(len(focused_elements), 1)
        self.assertNotEqual(first_all_app, focused_elements[0].id)

        # Hitting Tab should get us to the "menu bar" at the bottom
        ActionChains(self.driver).send_keys(Keys.TAB).perform()
        focused_elements = self.driver.find_elements(by=AppiumBy.XPATH, value="//page_tab[contains(@states, 'focused') and @name='Applications']")
        self.assertEqual(len(focused_elements), 1)

        # Hitting Right should get us to the "Places" page
        ActionChains(self.driver).send_keys(Keys.RIGHT).perform()
        self.driver.find_element(AppiumBy.NAME, "Frequently Used")

        # Hitting Right again should move focus to the power action button
        ActionChains(self.driver).send_keys(Keys.RIGHT).perform()
        try:
            self.driver.find_element(by=AppiumBy.XPATH, value="//push_button_menu[contains(@states, 'focused') and @name='Leave']")
        except NoSuchElementException:
            self.driver.find_element(by=AppiumBy.XPATH, value="//button[contains(@states, 'focused') and @name='Sleep']")


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