"""
Pyxelator - Pixel-based Element Locator
========================================

Visual element automation for Selenium & Playwright.
No more XPath hunting - just use screenshots!

Quick Start:
    from pyxelator import find, click, fill
    from selenium import webdriver

    driver = webdriver.Chrome()
    driver.get('https://example.com')

    # Simple as this!
    if find(driver, 'login_button.png'):
        click(driver, 'login_button.png')
        fill(driver, 'email_field.png', 'user@example.com')

Author: @idejongkok
Version: 0.1.0
"""

import cv2
import numpy as np
from typing import Tuple, Optional

__version__ = '0.1.0'
__all__ = ['find', 'locate', 'click', 'fill', 'exists', 'Pyxelator']


class Pyxelator:
    """
    Core class for visual element detection.

    Usually you don't need to use this directly - use module functions instead.
    """

    def __init__(self, driver):
        """Initialize with Selenium WebDriver or Playwright Page"""
        self.driver = driver
        self.driver_type = self._detect_driver_type()

    def _detect_driver_type(self) -> str:
        """Auto-detect Selenium or Playwright"""
        module = self.driver.__class__.__module__
        if 'selenium' in module.lower():
            return 'selenium'
        elif 'playwright' in module.lower() or self.driver.__class__.__name__ == 'Page':
            return 'playwright'
        return 'selenium'

    def _screenshot(self) -> bytes:
        """Get screenshot based on driver type"""
        return self.driver.screenshot() if self.driver_type == 'playwright' else self.driver.get_screenshot_as_png()

    def _locate(self, image: str, confidence: float = 0.7, grayscale: bool = True) -> Optional[Tuple[int, int]]:
        """
        Locate element by image template matching.

        Returns (x, y) coordinates or None if not found.
        """
        try:
            # Get and decode screenshot
            screenshot_bytes = self._screenshot()
            nparr = np.frombuffer(screenshot_bytes, np.uint8)
            img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
            img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

            # Load template
            template = cv2.imread(image, cv2.IMREAD_GRAYSCALE if grayscale else cv2.IMREAD_COLOR)
            if template is None:
                return None

            if not grayscale and len(template.shape) == 3:
                template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)

            h, w = template.shape[:2]
            img_h, img_w = img_gray.shape[:2]

            # Validate size
            if h > img_h or w > img_w:
                return None

            # Template matching
            result = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
            _, max_val, _, max_loc = cv2.minMaxLoc(result)

            if max_val >= confidence:
                return (max_loc[0] + w // 2, max_loc[1] + h // 2)

            # Try alternative method
            result_alt = cv2.matchTemplate(img_gray, template, cv2.TM_CCORR_NORMED)
            _, max_val_alt, _, max_loc_alt = cv2.minMaxLoc(result_alt)

            if max_val_alt >= confidence:
                return (max_loc_alt[0] + w // 2, max_loc_alt[1] + h // 2)

            return None

        except Exception:
            return None

    def find(self, image: str, confidence: float = 0.7) -> bool:
        """Check if element exists"""
        return self._locate(image, confidence) is not None

    def locate(self, image: str, confidence: float = 0.7) -> Optional[Tuple[int, int]]:
        """Get element coordinates"""
        return self._locate(image, confidence)

    def click(self, image: str, confidence: float = 0.7) -> bool:
        """Click element"""
        coords = self._locate(image, confidence)
        if not coords:
            return False

        x, y = coords

        if self.driver_type == 'playwright':
            script = f"""() => {{
                var el = document.elementFromPoint({x}, {y});
                if (el) {{ el.click(); return true; }}
                return false;
            }}"""
            return self.driver.evaluate(script)
        else:
            script = f"""
                var el = document.elementFromPoint({x}, {y});
                if (el) {{ el.click(); return true; }}
                return false;
            """
            return self.driver.execute_script(script)

    def fill(self, image: str, text: str, confidence: float = 0.7) -> bool:
        """Fill text into element"""
        coords = self._locate(image, confidence)
        if not coords:
            return False

        x, y = coords

        if self.driver_type == 'playwright':
            click_script = f"""() => {{
                var el = document.elementFromPoint({x}, {y});
                if (el) {{ el.focus(); el.click(); return true; }}
                return false;
            }}"""
            fill_script = f"""() => {{
                var el = document.elementFromPoint({x}, {y});
                if (el) {{
                    el.value = '{text}';
                    el.dispatchEvent(new Event('input', {{ bubbles: true }}));
                    el.dispatchEvent(new Event('change', {{ bubbles: true }}));
                    return true;
                }}
                return false;
            }}"""
            self.driver.evaluate(click_script)
            return self.driver.evaluate(fill_script)
        else:
            click_script = f"""
                var el = document.elementFromPoint({x}, {y});
                if (el) {{ el.focus(); el.click(); return true; }}
                return false;
            """
            fill_script = f"""
                var el = document.elementFromPoint({x}, {y});
                if (el) {{
                    el.value = '{text}';
                    el.dispatchEvent(new Event('input', {{ bubbles: true }}));
                    el.dispatchEvent(new Event('change', {{ bubbles: true }}));
                    return true;
                }}
                return false;
            """
            self.driver.execute_script(click_script)
            return self.driver.execute_script(fill_script)


# =============================================================================
# SIMPLE API - Use these functions directly!
# =============================================================================

def find(driver, image: str, confidence: float = 0.7) -> bool:
    """
    Check if element exists.

    Args:
        driver: Selenium WebDriver or Playwright Page
        image: Path to template image
        confidence: Match confidence 0.0-1.0 (default: 0.7)

    Returns:
        True if found, False otherwise

    Example:
        if find(driver, 'button.png'):
            print("Found!")
    """
    return Pyxelator(driver).find(image, confidence)


def locate(driver, image: str, confidence: float = 0.7) -> Optional[Tuple[int, int]]:
    """
    Get element coordinates.

    Args:
        driver: Selenium WebDriver or Playwright Page
        image: Path to template image
        confidence: Match confidence 0.0-1.0 (default: 0.7)

    Returns:
        (x, y) tuple if found, None otherwise

    Example:
        coords = locate(driver, 'button.png')
        if coords:
            print(f"Button at {coords}")
    """
    return Pyxelator(driver).locate(image, confidence)


def click(driver, image: str, confidence: float = 0.7) -> bool:
    """
    Click element by image.

    Args:
        driver: Selenium WebDriver or Playwright Page
        image: Path to template image
        confidence: Match confidence 0.0-1.0 (default: 0.7)

    Returns:
        True if clicked, False if not found

    Example:
        click(driver, 'submit.png')
    """
    return Pyxelator(driver).click(image, confidence)


def fill(driver, image: str, text: str, confidence: float = 0.7) -> bool:
    """
    Fill text into element.

    Args:
        driver: Selenium WebDriver or Playwright Page
        image: Path to template image
        text: Text to fill
        confidence: Match confidence 0.0-1.0 (default: 0.7)

    Returns:
        True if filled, False if not found

    Example:
        fill(driver, 'email.png', 'user@example.com')
    """
    return Pyxelator(driver).fill(image, text, confidence)


# Aliases
exists = find
