"""
Shinhan Card Scraper - Main implementation
This scraper logs into Shinhan Card and retrieves transaction data.
"""

import asyncio
import logging
from typing import Self

from .keyboards import NppfsKeyboard
from .playwright_mobile import PlaywrightSessionManager
from .types import CardTransaction

logger = logging.getLogger(__name__)


class ShinhanCardTransaction(CardTransaction):
    card_owner: str


class ShinhanCardScraper:
    """Scraper for Shinhan Card transaction information."""

    def __init__(self, headless: bool = False, base_url: str | None = None) -> None:
        self.base_url = base_url
        self._session_manager = PlaywrightSessionManager(headless, base_url)
        self._keyboard = NppfsKeyboard("pwd")
        self.browser = None
        self.context = None
        self.page = None

    async def __aenter__(self) -> Self:
        """Async context manager entry."""
        self.browser, self.context, self.page = await self._session_manager.__aenter__()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
        """Async context manager exit."""
        await self._session_manager.__aexit__(exc_type, exc_val, exc_tb)
        self.browser = None
        self.context = None
        self.page = None

    async def navigate_to_login(self) -> None:
        """Navigate to the Shinhan Card login page."""
        logger.debug("Navigating to Shinhan Card...")
        target_url = self.base_url or "https://www.shinhancard.com/mob/MOBFM045N/MOBFM045R01.shc?crustMenuId=ms117"
        await self.page.goto(target_url, wait_until="networkidle", timeout=60000)
        logger.debug("Login page loaded successfully")

    async def open_id_login_form(self) -> None:
        """Open the ID/password login form."""
        logger.debug("Opening ID login form...")

        # Click "다른 로그인 방식 선택"
        other_login = await self.page.query_selector("text=다른 로그인 방식 선택")
        if not other_login:
            raise Exception("Could not find 'other login method' link")

        await other_login.tap()
        logger.debug("  ✓ Opened login method selector")

        # Click "아이디" option (pesn-choice-btn05)
        logger.debug("  Selecting ID login option...")
        id_option = await self.page.query_selector("button.pesn-choice-btn05")
        if not id_option:
            raise Exception("Could not find ID login option button")

        await id_option.click()
        logger.debug("  ✓ ID login form opened")

    async def enter_user_id(self, user_id: str) -> None:
        """Enter user ID in the login form."""
        logger.debug("Entering user ID: %s", user_id)

        # Find the user ID input field
        user_id_input = await self.page.query_selector('input[id="t01.memid"]')
        if not user_id_input:
            user_id_input = await self.page.query_selector('input[type="text"]')

        if not user_id_input:
            raise Exception("Could not find user ID input field")

        # Clear and enter user ID
        await user_id_input.click()

        # Clear the field first
        await user_id_input.fill("")

        # Type the user ID character by character to trigger input events
        await user_id_input.type(user_id, delay=100)

        # Trigger input and change events to ensure form validation runs
        await user_id_input.evaluate("""(element) => {
            element.dispatchEvent(new Event('input', { bubbles: true }));
            element.dispatchEvent(new Event('change', { bubbles: true }));
            element.dispatchEvent(new Event('blur', { bubbles: true }));
        }""")

        logger.debug("User ID entered successfully")

    async def click_next_button(self) -> None:
        """Click the 'next' button to proceed to password entry."""
        logger.debug("Clicking '다음' button...")

        # Try to find button by ID starting with "next"
        next_button = await self.page.query_selector('button[id^="next"]')

        if not next_button:
            # Fallback to text-based search
            next_button = await self.page.query_selector('button:has-text("다음")')

        if not next_button:
            raise Exception("Could not find '다음' button")

        await next_button.tap()
        logger.debug("Moved to password entry screen")

    async def enter_password_via_keyboard(self, password: str) -> None:
        """
        Enter password using the on-screen keyboard.

        Args:
            password: The password to enter (alphanumeric and special characters)
        """
        await self._keyboard.enter_password(self.page, password)

    async def click_login_button(self) -> None:
        """Click the final login button."""
        logger.debug("Looking for login button...")

        # Find the login button by ID
        login_button = await self.page.query_selector("#loginBtn")

        if not login_button:
            # Fallback to text-based search
            login_button = await self.page.query_selector('button:has-text("로그인")')

        if not login_button:
            # Try finding by wider search
            all_buttons = await self.page.query_selector_all("button, a")
            for btn in all_buttons:
                try:
                    text = await btn.text_content()
                    if text and "로그인" in text.strip():
                        is_visible = await btn.is_visible()
                        if is_visible:
                            login_button = btn
                            break
                except Exception:
                    continue

        if not login_button:
            raise Exception("Could not find login button")

        logger.debug("Clicking login button (#loginBtn)...")
        await login_button.tap()
        logger.debug("Login button clicked")

    async def login(self, user_id: str, password: str) -> bool:
        """
        Perform login to Shinhan Card.

        Args:
            user_id: The user's ID
            password: The user's password
        """
        await self.navigate_to_login()
        await self.open_id_login_form()
        await self.enter_user_id(user_id)
        await self.click_next_button()
        await self.enter_password_via_keyboard(password)
        await self.click_login_button()

        # Check if login was successful
        current_url = self.page.url
        logger.debug("Current URL after login: %s", current_url)

        logger.debug("Login process completed")
        return True

    async def get_recent_transactions(self) -> list[ShinhanCardTransaction]:
        """
        Extract recent transaction information from the card usage history page.

        Returns:
            List of transaction dictionaries
        """
        logger.debug("Waiting for transactions to load...")

        # Wait for transactions to load (JavaScript populates the list)
        await asyncio.sleep(3)

        # Check if payList div is visible
        pay_list = await self.page.query_selector("#payList")
        if not pay_list:
            logger.debug("Transaction list not found")
            return []

        pay_list_style = await pay_list.get_attribute("style")
        if pay_list_style and "display: none" in pay_list_style or "display:none" in pay_list_style:
            logger.debug("Transaction list not loaded yet or no transactions available")
            return []

        logger.debug("Extracting transaction data...")

        # Get the transaction list container
        list_container = await pay_list.query_selector(".usagelist-wrap")
        if not list_container:
            logger.debug("Transaction list container not found")
            return []

        # Get all direct li children
        transaction_items = await list_container.query_selector_all("> li")
        transactions = []

        for item in transaction_items:
            # Find the button containing transaction info
            button = await item.query_selector(".btn-usagelist-view")
            if not button:
                continue

            # Extract date
            date_el = await button.query_selector(".txt-usage-date")
            date = await date_el.text_content() if date_el else ""
            date = date.strip() if date else ""

            # Extract merchant name
            merchant_el = await button.query_selector(".usagebox-list-tit")
            merchant = await merchant_el.text_content() if merchant_el else ""
            merchant = merchant.strip() if merchant else ""

            # Extract amount
            amount_el = await button.query_selector(".usagelist-list-amount")
            amount = await amount_el.text_content() if amount_el else ""
            amount = amount.strip() if amount else ""

            # Extract detail information
            card_owner = ""
            installment = ""
            status = ""

            detail_div = await button.query_selector(".usagelist-detail")
            if detail_div:
                # Get all spans
                all_spans = await detail_div.query_selector_all("span")
                detail_items = []

                for span in all_spans:
                    # The first span in 'usagelist-detail' sometimes contains a 'paytime' class
                    # with redundant date/time information that would incorrectly be parsed
                    # as 'card_owner'. Skip this span based on actual HTML observation.
                    class_attr = await span.get_attribute("class")
                    if class_attr and "paytime" in class_attr:
                        continue

                    text = await span.text_content()
                    text = text.strip() if text else ""

                    if text:
                        detail_items.append(text)

                # Map to meaningful names
                if len(detail_items) > 0:
                    card_owner = detail_items[0]
                if len(detail_items) > 1:
                    installment = detail_items[1]

                # Check remaining items for status keywords
                for item in detail_items[2:]:
                    if "취소" in item:
                        status = "취소"

            # Only add if we have date AND (merchant OR amount)
            if date and (merchant or amount):
                transactions.append(
                    {
                        "transacted_at": date,  # 'transacted_at': '2025.11.16 13:02',
                        "merchant": merchant,
                        "amount": amount,
                        "card_owner": card_owner,
                        "installment": installment,
                        "status": status,
                    }
                )

        logger.debug("✓ Extracted %d transactions", len(transactions))

        return transactions
