from appium.webdriver.common.appiumby import AppiumBy

from puma.apps.android.teleguard import logger
from puma.state_graph.puma_driver import PumaDriver, supported_version
from puma.state_graph.action import action
from puma.state_graph.state_graph import StateGraph
from puma.state_graph.state import ContextualState, SimpleState, compose_clicks

APPLICATION_PACKAGE = 'ch.swisscows.messenger.teleguardapp'

CONVERSATION_STATE_TELEGUARD_HEADER = '//android.view.View[@content-desc="TeleGuard"]'
CONVERSATION_STATE_HAMBURGER_MENU = '//android.widget.FrameLayout[@resource-id="android:id/content"]/android.widget.FrameLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.view.View[1]/android.view.View[2]/android.view.View[3]'
CONVERSATION_STATE_SETTINGS_BUTTON = '//android.widget.ImageView[@content-desc="Settings"]'
CONVERSATION_STATE_ABOUT_BUTTON = '//android.widget.ImageView[@content-desc="About"]'
CONVERSATION_STATE_TELEGUARD_STATUS = '//android.view.View[@content-desc="Online"]|//android.view.View[contains(@content-desc, "Connection to server")]'
CONVERSATION_STATE_ADD_CONTACT = '//android.widget.ImageView[@content-desc="Add contact"]'
CONVERSATION_STATE_EDIT_TEXT = '//android.widget.EditText'
CONVERSATION_STATE_INVITE = '//android.widget.Button[@content-desc="INVITE"]'
CONVERSATION_STATE_YOU_HAVE_BEEN_INVITED = '//android.view.View[contains(@content-desc, "You have been invited")]'
CONVERSATION_STATE_ACCEPT_INVITE = '//android.widget.Button[@content-desc="ACCEPT INVITE"]'

CHAT_STATE_CONVERSATION_NAME = ('//android.widget.FrameLayout[@resource-id="android:id/content"]/android.widget.FrameLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.view.View[1]/android.view.View[2]/android.widget.ImageView[2][@content-desc]|'
                                '//android.widget.FrameLayout[@resource-id="android:id/content"]/android.widget.FrameLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.view.View[1]/android.view.View[2]/android.view.View[1][@content-desc]')
CHAT_STATE_TEXT_FIELD = '//android.widget.EditText'
CHAT_STATE_MICROPHONE_BUTTON = '//android.widget.FrameLayout[@resource-id="android:id/content"]/android.widget.FrameLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.widget.ImageView[4]'
CHAT_STATE_SEND_BUTTON = '//android.widget.FrameLayout[@resource-id="android:id/content"]/android.widget.FrameLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.widget.ImageView[3]'

SETTINGS_STATE_CHANGE_TELE_GUARD_ID = '//android.view.View[@content-desc="Change TeleGuard ID"]'

ABOUT_STATE_ABOUT = '//android.view.View[@content-desc="About"]'
ABOUT_STATE_TERMS_OF_USE = '//android.view.View[@content-desc=" Terms of use"]'

def go_to_chat(driver: PumaDriver, conversation: str):
    """
    Navigates to a specific chat conversation in the application.

    This function constructs an XPath to locate and click on a conversation element
    based on the conversation name. It is designed to be used within a state transition
    to navigate to a specific chat state.

    :param driver: The PumaDriver instance used to interact with the application.
    :param conversation: The name of the conversation to navigate to.
    """
    xpath = f'//android.widget.ImageView[contains(lower-case(@content-desc), "{conversation.lower()}")] | ' \
            f'//android.view.View[contains(lower-case(@content-desc), "{conversation.lower()}")]'
    driver.driver.find_elements(by=AppiumBy.XPATH, value=xpath)[-1].click()

class TeleGuardChatState(SimpleState, ContextualState):
    """
    A state representing a chat screen in the application.

    This class extends both SimpleState and ContextualState to represent a chat screen
    and validate its context based on the conversation name.
    """

    def __init__(self, parent_state):
        """
        Initializes the ChatState with a parent state.

        :param parent_state: The parent state of this chat state.
        """
        super().__init__(xpaths=[CHAT_STATE_CONVERSATION_NAME, CHAT_STATE_MICROPHONE_BUTTON, CHAT_STATE_TEXT_FIELD],
                         parent_state=parent_state)

    def validate_context(self, driver: PumaDriver, conversation: str = None) -> bool:
        """
        Validates the context of the chat state.

        This method checks if the current chat screen matches the expected conversation name.

        :param driver: The PumaDriver instance used to interact with the application.
        :param conversation: The name of the conversation to validate against.
        :return: True if the context is valid, False otherwise.
        """
        if not conversation:
            return True

        content_desc = (driver.get_element(CHAT_STATE_CONVERSATION_NAME).get_attribute('content-desc'))
        return conversation.lower() in content_desc.lower()

@supported_version('4.0.7')
class TeleGuard(StateGraph):
    """
    A class representing a state graph for managing UI states and transitions in the TeleGuard application.

    This class uses a state machine approach to manage transitions between different states
    of the TeleGuard user interface. It provides methods to navigate between states, validate states,
    and handle unexpected states or errors.
    """

    conversations_state = SimpleState( [CONVERSATION_STATE_TELEGUARD_HEADER, CONVERSATION_STATE_HAMBURGER_MENU, CONVERSATION_STATE_TELEGUARD_STATUS], initial_state=True)
    chat_state = TeleGuardChatState(parent_state=conversations_state)
    settings_state = SimpleState([SETTINGS_STATE_CHANGE_TELE_GUARD_ID], parent_state=conversations_state)
    about_screen_state = SimpleState([ABOUT_STATE_ABOUT, ABOUT_STATE_TERMS_OF_USE], parent_state=conversations_state)

    conversations_state.to(chat_state, go_to_chat)
    conversations_state.to(settings_state, compose_clicks([CONVERSATION_STATE_HAMBURGER_MENU, CONVERSATION_STATE_SETTINGS_BUTTON], name='go_to_settings'))
    conversations_state.to(about_screen_state, compose_clicks([CONVERSATION_STATE_HAMBURGER_MENU, CONVERSATION_STATE_ABOUT_BUTTON], name='go_to_about'))

    def __init__(self, device_udid):
        """
        Initializes the TestFsm with a device UDID.

        This class provides an API for interacting with the TeleGuard application.
        It can be used with an emulator or a real device attached to the computer.

        :param device_udid: The unique device identifier for the Android device.
        """
        StateGraph.__init__(self, device_udid, APPLICATION_PACKAGE)

    @action(chat_state)
    def send_message(self, msg: str, conversation: str = None):
        """
        Sends a message in the current chat conversation.

        :param msg: The message to send.
        :param conversation: The name of the conversation to send the message in.
        """
        self.driver.click(CHAT_STATE_TEXT_FIELD)
        self.driver.send_keys(CHAT_STATE_TEXT_FIELD, msg)
        self.driver.click(CHAT_STATE_SEND_BUTTON)

    @action(conversations_state)
    def add_contact(self, teleguard_id: str):
        """
        Adds a contact by TeleGuard ID.

        :param teleguard_id: The TeleGuard ID of the contact to add.
        """
        self.driver.click(CONVERSATION_STATE_HAMBURGER_MENU)
        self.driver.click(CONVERSATION_STATE_ADD_CONTACT)
        self.driver.send_keys(CONVERSATION_STATE_EDIT_TEXT, teleguard_id)
        self.driver.click(CONVERSATION_STATE_INVITE)

    @action(conversations_state)
    def accept_invite(self):
        """
        Accepts an invite from another user.

        If there are multiple invites, only the topmost invite in the UI will be accepted.
        """
        self.driver.swipe_to_click_element(CONVERSATION_STATE_YOU_HAVE_BEEN_INVITED)
        self.driver.click(CONVERSATION_STATE_ACCEPT_INVITE)
