"""Foreground app detection using PyWinCtl.

Detect meeting applications in the foreground window.
"""

from dataclasses import dataclass, field

from noteflow.domain.triggers.entities import TriggerSignal, TriggerSource
from noteflow.infrastructure.logging import get_logger

logger = get_logger(__name__)


@dataclass
class ForegroundAppSettings:
    """Configuration for foreground app detection.

    Attributes:
        enabled: Whether foreground app detection is enabled.
        weight: Confidence weight contributed by this provider.
        meeting_apps: Set of app name substrings to match (lowercase).
        suppressed_apps: Apps to ignore even if they match meeting_apps.
    """

    enabled: bool
    weight: float
    meeting_apps: set[str] = field(default_factory=set)
    suppressed_apps: set[str] = field(default_factory=set)

    def __post_init__(self) -> None:
        self.meeting_apps = {app.lower() for app in self.meeting_apps}
        self.suppressed_apps = {app.lower() for app in self.suppressed_apps}


class ForegroundAppProvider:
    """Detect meeting apps in foreground using PyWinCtl.

    PyWinCtl provides cross-platform active window detection for
    Linux (X11/Wayland), macOS, and Windows.
    """

    def __init__(self, settings: ForegroundAppSettings) -> None:
        """Initialize foreground app provider.

        Args:
            settings: Configuration settings for foreground app detection.
        """
        self._settings = settings
        self._available: bool | None = None

    @property
    def source(self) -> TriggerSource:
        """Get the source type for this provider."""
        return TriggerSource.FOREGROUND_APP

    @property
    def max_weight(self) -> float:
        """Get the maximum weight this provider can contribute."""
        return self._settings.weight

    def is_enabled(self) -> bool:
        """Check if this provider is enabled and available."""
        return self._settings.enabled and self._is_available()

    def _is_available(self) -> bool:
        """Check if PyWinCtl is available and working."""
        if self._available is not None:
            return self._available

        try:
            import pywinctl

            # Try to get active window to verify it works
            _ = pywinctl.getActiveWindow()
            self._available = True
            logger.debug("PyWinCtl available for foreground detection")
        except ImportError:
            self._available = False
            logger.warning("PyWinCtl not installed - foreground detection disabled")
        # INTENTIONAL BROAD HANDLER: Platform interop via pywinctl
        # - Different platforms raise different exceptions
        # - Must gracefully degrade if window detection fails
        except Exception as e:
            self._available = False
            logger.warning("PyWinCtl unavailable: %s - foreground detection disabled", e)

        return self._available

    def get_signal(self) -> TriggerSignal | None:
        """Get current signal if meeting app is in foreground.

        Returns:
            TriggerSignal if a meeting app is detected, None otherwise.
        """
        if not self.is_enabled():
            return None

        try:
            import pywinctl

            window = pywinctl.getActiveWindow()
            if not window:
                return None

            title = window.title
            if not title:
                return None

            title_lower = title.lower()

            # Check if app is suppressed
            for suppressed in self._settings.suppressed_apps:
                if suppressed in title_lower:
                    return None

            # Check if it's a meeting app
            for app in self._settings.meeting_apps:
                if app in title_lower:
                    return TriggerSignal(
                        source=self.source,
                        weight=self.max_weight,
                        app_name=title,
                    )

        # INTENTIONAL BROAD HANDLER: Platform interop via pywinctl
        # - Window APIs can fail unpredictably across platforms
        # - Must not crash signal detection loop
        except Exception as e:
            logger.debug("Foreground detection error: %s", e)

        return None

    def suppress_app(self, app_name: str) -> None:
        """Add an app to the suppression list.

        Args:
            app_name: App name substring to suppress (will be lowercased).
        """
        self._settings.suppressed_apps.add(app_name.lower())
        logger.info("Suppressed app: %s", app_name)

    def unsuppress_app(self, app_name: str) -> None:
        """Remove an app from the suppression list.

        Args:
            app_name: App name substring to unsuppress.
        """
        self._settings.suppressed_apps.discard(app_name.lower())

    @property
    def suppressed_apps(self) -> frozenset[str]:
        """Get the current set of suppressed app names.

        Returns:
            Frozenset of lowercased app name substrings being suppressed.
        """
        return frozenset(self._settings.suppressed_apps)

    @property
    def meeting_apps(self) -> frozenset[str]:
        """Get the current set of meeting app names.

        Returns:
            Frozenset of lowercased app name substrings being detected.
        """
        return frozenset(self._settings.meeting_apps)

    def add_meeting_app(self, app_name: str) -> None:
        """Add an app to the meeting apps list.

        Args:
            app_name: App name substring to add (will be lowercased).
        """
        self._settings.meeting_apps.add(app_name.lower())
