"""Restricted web view with URL whitelist enforcement for Kiosk-mode."""

import fnmatch
import logging
from pathlib import Path
from typing import List, Optional, Union

from PySide6.QtCore import QStandardPaths, QUrl
from PySide6.QtGui import QDesktopServices
from PySide6.QtWebEngineCore import QWebEngineNavigationRequest, QWebEnginePage, QWebEngineProfile
from PySide6.QtWebEngineWidgets import QWebEngineView

logger = logging.getLogger(__name__)


logger = logging.getLogger(__name__)


class CustomWebEnginePage(QWebEnginePage):
    """Custom page that handles new window requests for downloads."""

    def acceptNavigationRequest(
        self, url: Union[QUrl, str], nav_type: QWebEnginePage.NavigationType, is_main_frame: bool
    ) -> bool:
        """Handle navigation requests, including download links.

        Args:
            url: Target URL (QUrl or string)
            nav_type: Type of navigation (link click, form submit, etc.)
            is_main_frame: Whether this is the main frame

        Returns:
            True to accept navigation, False to reject
        """
        # Convert to string if QUrl
        url_str = url.toString() if isinstance(url, QUrl) else url

        # Log all navigation attempts for debugging
        logger.debug(f"Navigation request: {url_str} (type={nav_type}, main_frame={is_main_frame})")

        # Check if this might be a download (common file extensions)
        download_extensions = [
            ".pdf",
            ".zip",
            ".rar",
            ".7z",
            ".tar",
            ".gz",
            ".doc",
            ".docx",
            ".xls",
            ".xlsx",
            ".ppt",
            ".pptx",
            ".jpg",
            ".jpeg",
            ".png",
            ".gif",
            ".bmp",
            ".svg",
            ".mp4",
            ".mp3",
            ".avi",
            ".mov",
            ".wav",
            ".exe",
            ".msi",
            ".dmg",
            ".pkg",
            ".csv",
            ".txt",
            ".json",
            ".xml",
        ]

        if any(url_str.lower().endswith(ext) for ext in download_extensions):
            logger.debug(f"Detected potential download URL: {url_str}")
            # This will trigger downloadRequested if it's a download

        return super().acceptNavigationRequest(url, nav_type, is_main_frame)

    def createWindow(self, window_type: QWebEnginePage.WebWindowType) -> QWebEnginePage:
        """Handle new window requests (target=_blank, window.open, etc.).

        Many downloads are triggered via target="_blank" links.

        Args:
            window_type: Type of window being created

        Returns:
            New page instance for the window
        """
        logger.debug(f"New window requested, type: {window_type}")

        # Create a temporary page to handle the download
        # This page will never be displayed but allows downloads to work
        download_page = QWebEnginePage(self.profile(), self)

        logger.debug("Created temporary page for download/popup")

        # Return the temporary page - it will trigger downloadRequested if it's a download
        return download_page


class RestrictedWebEngineView(QWebEngineView):
    """Web view that enforces URL whitelist for Kiosk-mode security.

    If allowed_urls is empty, no restrictions are applied.
    If allowed_urls is not empty, only matching URLs are loaded in the view.
    Non-matching URLs open in the system default browser.
    """

    def __init__(self, allowed_urls: Optional[List[str]] = None):
        """Initialize the restricted web view.

        Args:
            allowed_urls: List of allowed URL patterns (empty = no restriction)
                         Patterns support wildcards: *.example.com, localhost, etc.
        """
        super().__init__()
        self.allowed_urls = allowed_urls or []

        # Create persistent profile for cookie and session storage
        self.profile = self._create_persistent_profile()

        # Use custom page for better download handling with persistent profile
        custom_page = CustomWebEnginePage(self.profile, self)
        self.setPage(custom_page)

        logger.debug(
            "RestrictedWebEngineView initialized with CustomWebEnginePage and persistent profile"
        )

        # Connect to navigation request handler
        self.page().navigationRequested.connect(self._on_navigation_requested)

    def _create_persistent_profile(self) -> QWebEngineProfile:
        """Create and configure a persistent web engine profile.

        This enables persistent cookies and cache storage, allowing
        authentication sessions (e.g., Microsoft login) to persist
        across application restarts.

        Returns:
            Configured QWebEngineProfile with persistent storage
        """
        # Get application data directory
        app_data_dir = QStandardPaths.writableLocation(
            QStandardPaths.StandardLocation.AppDataLocation
        )

        # Create profile directory path
        profile_path = Path(app_data_dir) / "WebEngineProfile"
        profile_path.mkdir(parents=True, exist_ok=True)

        # Create persistent profile with custom storage location
        # Using "WebDropBridge" as the profile name
        # Note: No parent specified so we control the lifecycle
        profile = QWebEngineProfile("WebDropBridge")
        profile.setPersistentStoragePath(str(profile_path))

        # Configure persistent cookies (critical for authentication)
        profile.setPersistentCookiesPolicy(
            QWebEngineProfile.PersistentCookiesPolicy.ForcePersistentCookies
        )

        # Enable HTTP cache for better performance
        profile.setHttpCacheType(QWebEngineProfile.HttpCacheType.DiskHttpCache)

        # Set cache size to 100 MB
        profile.setHttpCacheMaximumSize(100 * 1024 * 1024)

        logger.debug(f"Created persistent profile at: {profile_path}")
        logger.debug("Cookies policy: ForcePersistentCookies")
        logger.debug("HTTP cache: DiskHttpCache (100 MB)")

        return profile

    def _on_navigation_requested(self, request: QWebEngineNavigationRequest) -> None:
        """Handle navigation requests and enforce URL whitelist.

        Args:
            request: Navigation request to process
        """
        url = request.url()

        # If no restrictions, allow all URLs
        if not self.allowed_urls:
            return

        # Check if URL matches whitelist
        if self._is_url_allowed(url):
            # Allow the navigation (default behavior)
            return

        # URL not whitelisted - open in system browser
        request.reject()
        QDesktopServices.openUrl(url)

    def _is_url_allowed(self, url: QUrl) -> bool:
        """Check if a URL matches the whitelist.

        Supports:
        - Exact domain matches: example.com
        - Wildcard patterns: *.example.com
        - Localhost variations: localhost, 127.0.0.1
        - File URLs: file://...

        Args:
            url: QUrl to check

        Returns:
            True if URL is allowed, False otherwise
        """
        url_str = url.toString()
        host = url.host()
        scheme = url.scheme()

        # Allow file:// URLs (local webapp)
        if scheme == "file":
            return True

        # If no whitelist, allow all URLs
        if not self.allowed_urls:
            return True

        # Check against whitelist patterns
        for pattern in self.allowed_urls:
            # Check full URL pattern
            if fnmatch.fnmatch(url_str, pattern):
                return True

            # Check host pattern (for http/https URLs)
            if scheme in ("http", "https") and host:
                if fnmatch.fnmatch(host, pattern):
                    return True

                # Also check with www prefix variants
                if fnmatch.fnmatch(f"www.{host}", pattern):
                    return True
                if fnmatch.fnmatch(host.replace("www.", ""), pattern):
                    return True

        return False
