"""NPPFS keyboard strategy for Shinhan Card and Hyundai Card scrapers."""

import asyncio
import logging

from playwright.async_api import Page

from ..errors import KeyboardError
from .base import KeyboardStrategy

logger = logging.getLogger(__name__)


class NppfsKeyboard(KeyboardStrategy):
    """
    Keyboard strategy for NPPFS (NProtect Keypad For SecureWeb) keyboard.

    Used by Shinhan Card and Hyundai Card.
    - Uses #nppfs-keypad-{field_id} selectors
    - Image-based buttons with aria-label attributes
    - Mode switching between normal and special characters
    """

    def __init__(self, field_id: str):
        """
        Initialize NPPFS keyboard.

        Args:
            field_id: The field ID for the keyboard (e.g., "pwd", "webPwd", "cardPwd")
        """
        self.field_id = field_id
        self.keyboard_selector = f"#nppfs-keypad-{field_id}"

    async def enter_password(self, page: Page, password: str) -> None:
        """Enter password using NPPFS keyboard."""
        logger.debug("Clicking password field to open NPPFS keyboard...")

        # Find and click password field using the exact field_id (e.g., webPwd or cardPwd)
        password_field = await page.query_selector(f"input#{self.field_id}")
        if not password_field:
            password_field = await page.query_selector(f'input[name="{self.field_id}"]')

        if not password_field:
            raise KeyboardError(f"Could not find password field for {self.field_id}")

        await password_field.tap()

        # Wait for keyboard to appear
        await page.wait_for_selector(self.keyboard_selector, timeout=10000, state="attached")
        logger.debug("NPPFS keyboard appeared")

        # Enter password characters
        await self._enter_text(page, password)

        # Close keyboard
        await self._close_keyboard(page)

        logger.debug("Password entered successfully")

    async def enter_pin(self, page: Page, pin: str) -> None:
        """Enter numeric PIN using NPPFS keyboard."""
        logger.debug("Entering PIN via NPPFS keyboard...")

        # Find and click PIN input
        pin_input = await page.query_selector(f"input#{self.field_id}")
        if not pin_input:
            pin_input = await page.query_selector(f'input[name="{self.field_id}"]')
        if not pin_input:
            pin_input = await page.query_selector('input[type="password"]')

        if not pin_input:
            raise KeyboardError("Could not find PIN input field")

        # Check if visible
        is_visible = await pin_input.is_visible()
        if not is_visible:
            raise KeyboardError("PIN input field not visible")

        logger.debug("Found PIN input field, tapping to open keyboard...")
        await pin_input.tap()
        await asyncio.sleep(2)

        # Enter each digit
        for i, digit in enumerate(pin):
            logger.debug("Entering digit %d/%d: %s", i + 1, len(pin), digit)

            button = await page.query_selector(f'{self.keyboard_selector} img[aria-label="{digit}"]')
            if not button:
                button = await page.query_selector(f'{self.keyboard_selector} img[alt="{digit}"]')
            if not button:
                button = await page.query_selector(f'img[aria-label="{digit}"]')
            if not button:
                button = await page.query_selector(f'img[alt="{digit}"]')

            if button:
                is_visible = await button.is_visible()
                if is_visible:
                    await button.tap()
                    logger.debug("  ✓ Tapped '%s'", digit)
                    await asyncio.sleep(0.2)
                else:
                    raise KeyboardError(f"Button not visible for PIN digit: {digit}")
            else:
                raise KeyboardError(f"Could not find button for PIN digit: {digit}")

        logger.debug("PIN entered successfully")

    async def _enter_text(self, page: Page, text: str) -> None:
        """Enter text using NPPFS keyboard."""
        for i, char in enumerate(text):
            logger.debug("Entering character %d/%d: %s", i + 1, len(text), char)

            # Determine aria-label for this character
            aria_label = self._get_aria_label(char)

            # Find button with multiple fallback selectors
            selector = f'{self.keyboard_selector} img[aria-label="{aria_label}"]'
            button = await page.query_selector(selector)

            if not button:
                selector = f'{self.keyboard_selector} img[alt="{aria_label}"]'
                button = await page.query_selector(selector)

            if button:
                is_visible = await button.is_visible()
                if is_visible:
                    await button.tap()
                    logger.debug("  ✓ Tapped '%s'", aria_label)
                    await asyncio.sleep(0.2)
                else:
                    # Button not visible, need to switch mode
                    await self._switch_keyboard_mode(page, char)
                    # Try finding button again
                    button = await self._find_button_after_mode_switch(page, aria_label)
                    if button:
                        await button.tap()
                        logger.debug("  ✓ Tapped '%s'", aria_label)
                        await asyncio.sleep(0.2)
                    else:
                        raise KeyboardError(f"Button not found after mode switch for character: {char}")
            else:
                raise KeyboardError(f"Could not find button for character: {char}")

    def _get_aria_label(self, char: str) -> str:
        """Get the aria-label for a character."""
        if char.islower():
            return f"소문자 {char}"
        elif char.isupper():
            return f"대문자 {char}"
        elif char.isdigit():
            return char
        elif char in self.SPECIAL_CHAR_KOREAN:
            return self.SPECIAL_CHAR_KOREAN[char]
        else:
            return char

    async def _switch_keyboard_mode(self, page: Page, char: str) -> None:
        """Switch keyboard mode based on character type."""
        if char in self.SPECIAL_CHAR_KOREAN:
            # Switch to special mode
            logger.debug("  Switching to special mode for '%s'...", char)
            special_toggle = await page.query_selector('img[aria-label="특수문자"]')
            if not special_toggle:
                special_toggle = await page.query_selector('img[alt="특수문자"]')

            if special_toggle:
                is_toggle_visible = await special_toggle.is_visible()
                if not is_toggle_visible:
                    # Find visible toggle
                    all_toggles = await page.query_selector_all('img[aria-label="특수문자"]')
                    for toggle in all_toggles:
                        if await toggle.is_visible():
                            special_toggle = toggle
                            is_toggle_visible = True
                            break

                if is_toggle_visible:
                    await special_toggle.tap()
                    logger.debug("  Tapped special character toggle")
                    await asyncio.sleep(1)
                else:
                    raise KeyboardError("Special character toggle not visible")
            else:
                raise KeyboardError("Special character toggle not found")
        else:
            # Switch to normal mode
            logger.debug("  Switching to normal mode for '%s'...", char)
            normal_toggle = await page.query_selector('img[aria-label="소문자"]')
            if not normal_toggle:
                normal_toggle = await page.query_selector('img[alt="소문자"]')
            if not normal_toggle:
                normal_toggle = await page.query_selector('img[aria-label="대문자"]')
            if not normal_toggle:
                normal_toggle = await page.query_selector('img[alt="대문자"]')

            if normal_toggle:
                is_toggle_visible = await normal_toggle.is_visible()
                if not is_toggle_visible:
                    all_toggles = await page.query_selector_all('img[aria-label="소문자"]')
                    all_toggles.extend(await page.query_selector_all('img[aria-label="대문자"]'))
                    for toggle in all_toggles:
                        if await toggle.is_visible():
                            normal_toggle = toggle
                            is_toggle_visible = True
                            break

                if is_toggle_visible:
                    await normal_toggle.tap()
                    logger.debug("  Tapped normal mode toggle")
                    await asyncio.sleep(1)
                else:
                    raise KeyboardError("Normal mode toggle not visible")
            else:
                raise KeyboardError("Normal mode toggle not found")

    async def _find_button_after_mode_switch(self, page: Page, aria_label: str) -> Page:
        """Find button after mode switch with retry logic."""
        button = None
        for attempt in range(15):  # Try for 3 seconds
            button = await page.query_selector(f'img[aria-label="{aria_label}"]')
            if not button:
                button = await page.query_selector(f'img[alt="{aria_label}"]')

            if button:
                is_button_visible = await button.is_visible()
                if is_button_visible:
                    logger.debug("  Found visible button for '%s' after %.1fs", aria_label, attempt * 0.2)
                    return button
            await asyncio.sleep(0.2)

        return button

    async def _close_keyboard(self, page: Page) -> None:
        """Close the NPPFS keyboard."""
        confirm_selector = f'{self.keyboard_selector} img[aria-label="확인"]'
        confirm_button = await page.query_selector(confirm_selector)

        if not confirm_button:
            confirm_button = await page.query_selector(f'{self.keyboard_selector} img[alt="확인"]')

        if confirm_button:
            logger.debug("Clicking confirm button...")
            await confirm_button.tap()
            logger.debug("Keyboard closed")
        else:
            logger.warning("Confirm button not found, continuing anyway...")
