"""Android-specific mobile service implementation"""

from __future__ import annotations

from appium.webdriver.common.appiumby import AppiumBy

from noqa_runner.application.services.base_mobile_service import BaseMobileService
from noqa_runner.domain.models.app_source import AppSource
from noqa_runner.domain.models.state.screen import ActiveElement
from noqa_runner.logging_config import get_logger

logger = get_logger(__name__)


class AndroidMobileService(BaseMobileService):
    """Android-specific mobile service implementation"""

    @staticmethod
    async def resolve_bundle_id(app_build_path: str | None) -> str | None:
        """
        Resolve bundle ID for Android platform.
        Android doesn't support extracting bundle ID from build, returns None.
        """
        return None

    def install_app(
        self,
        build_source: AppSource | None,
        bundle_id: str | None,
        app_store_id: str | None,
    ) -> None:
        """
        Install app based on build_source.
        Android doesn't support TestFlight/App Store installation.
        This method does nothing for Android.
        """
        pass

    def is_app_crashed(self, xml_source: str, action_types: list[str]) -> bool:
        """
        Check if Android app has crashed.
        Android crash detection not implemented yet.

        Args:
            xml_source: XML source of current screen
            action_types: List of action type names from test steps

        Returns:
            False (crash detection not implemented for Android)
        """
        return False

    def _escape_uiautomator_string(self, value: str) -> str:
        """
        Escape string for Android UIAutomator.

        Escapes backslashes first, then control characters (newline, tab, carriage return,
        backspace, formfeed), and finally double quotes to ensure proper UIAutomator parsing.

        Returns string wrapped in double quotes.
        """
        # Escape backslashes first to avoid double-escaping
        escaped = value.replace("\\", "\\\\")
        # Escape control characters by replacing with literal escape sequences
        escaped = escaped.replace("\n", "\\n")
        escaped = escaped.replace("\t", "\\t")
        escaped = escaped.replace("\r", "\\r")
        escaped = escaped.replace("\b", "\\b")
        escaped = escaped.replace("\f", "\\f")
        # Escape double quotes last
        escaped = escaped.replace('"', '\\"')
        return f'"{escaped}"'

    def get_locator(
        self, element: ActiveElement, *, for_input: bool = False
    ) -> tuple[str, str] | None:
        """
        Generate Android locator for Appium element lookup.

        Args:
            element: ActiveElement to generate locator for
            for_input: If True, generate locator optimized for text input

        Returns:
            Tuple of (strategy, locator) or None if element is not from Appium
            - For resource-id and text: (ANDROID_UIAUTOMATOR, UIAutomator selector)
            - For bounds: (XPATH, XPath selector)

        Example selectors:
            UIAutomator: new UiSelector().text("Login")
            UIAutomator: new UiSelector().resourceId("com.example:id/username")
            XPath: //*[@class="android.widget.ScrollView"][@bounds="[0,286][720,856]"]
        """
        if element.source != "appium" or not element.appium_data:
            return None

        # If valid resource-id exists, use it (most reliable for Android)
        if element.appium_data.resource_id:
            id_escaped = self._escape_uiautomator_string(
                element.appium_data.resource_id
            )
            locator = f"new UiSelector().resourceId({id_escaped})"
            return (AppiumBy.ANDROID_UIAUTOMATOR, locator)

        # Build selector using available attributes
        selectors: list[str] = []

        # Add class name if present
        if element.appium_data.type:
            type_escaped = self._escape_uiautomator_string(element.appium_data.type)
            selectors.append(f"className({type_escaped})")

        # Add text if present (for text fields, this is the content)
        if element.appium_data.text and not for_input:
            # Don't use text for input fields as it may change during typing
            text_escaped = self._escape_uiautomator_string(element.appium_data.text)
            selectors.append(f"text({text_escaped})")

        # If no text but has bounds, use XPath with bounds
        if (
            not element.appium_data.text
            and not element.appium_data.label
            and element.appium_data.bounds
            and not for_input
        ):
            # Use XPath with bounds as fallback
            xpath_parts = []
            if element.appium_data.type:
                xpath_parts.append(f'[@class="{element.appium_data.type}"]')
            xpath_parts.append(f'[@bounds="{element.appium_data.bounds}"]')
            locator = "//*" + "".join(xpath_parts)
            return (AppiumBy.XPATH, locator)

        if not selectors:
            return None

        # Combine selectors for UIAutomator
        locator = "new UiSelector()." + ".".join(selectors)
        return (AppiumBy.ANDROID_UIAUTOMATOR, locator)

    def _has_overlay_ui(self, xml_source: str) -> bool:
        """Check if XML contains Android notification overlays"""
        # Android notifications typically have these package names
        return (
            "com.android.systemui" in xml_source
            or "StatusBarNotification" in xml_source
        )

    async def input_text_in_element(
        self, element: ActiveElement, text: str, elements_tree: str | None = None
    ) -> None:
        """
        Android-specific text input - uses 'text' attribute instead of 'value'

        Args:
            element: Target element for text input
            text: Text to input
            elements_tree: XML tree to check for keyboard presence (unused)
        """
        locator_result = self.get_locator(element, for_input=True)
        if locator_result:
            strategy, locator = locator_result
            self.client.send_keys(
                by=strategy, locator=locator, text=text, value_field="text"
            )
