"""BetterCam-based screen capture engine (Windows only, DXGI Desktop Duplication)."""

import sys
import time
from typing import Any, Dict, List, Optional


from wled_controller.core.capture_engines.base import (
    CaptureEngine,
    CaptureStream,
    DisplayInfo,
    ScreenCapture,
)
from wled_controller.utils import get_logger

logger = get_logger(__name__)

_REINIT_COOLDOWN = 3.0  # seconds between re-init attempts


class BetterCamCaptureStream(CaptureStream):
    """BetterCam capture stream for a specific display."""

    def __init__(self, display_index: int, config: Dict[str, Any]):
        super().__init__(display_index, config)
        self._camera = None
        self._bettercam = None
        self._last_reinit: float = 0

    def initialize(self) -> None:
        try:
            import bettercam
            self._bettercam = bettercam
        except ImportError:
            raise RuntimeError(
                "BetterCam not installed. Install with: pip install bettercam"
            )

        # Clear global camera cache for fresh DXGI state
        try:
            self._bettercam.__factory.clean_up()
        except Exception as e:
            logger.debug("BetterCam factory cleanup on init: %s", e)
            pass

        self._camera = self._bettercam.create(
            output_idx=self.display_index,
            output_color="RGB",
        )

        if not self._camera:
            raise RuntimeError(f"Failed to create BetterCam camera for display {self.display_index}")

        self._initialized = True
        self._last_reinit = time.monotonic()
        logger.info(f"BetterCam capture stream initialized (display={self.display_index})")

    def cleanup(self) -> None:
        if self._camera:
            try:
                if self._camera.is_capturing:
                    self._camera.stop()
            except Exception as e:
                logger.debug("BetterCam camera stop during cleanup: %s", e)
                pass
            try:
                self._camera.release()
            except Exception as e:
                logger.error(f"Error releasing BetterCam camera: {e}")
            self._camera = None

        if self._bettercam:
            try:
                self._bettercam.__factory.clean_up()
            except Exception as e:
                logger.debug("BetterCam factory cleanup on teardown: %s", e)
                pass

        self._initialized = False
        logger.info(f"BetterCam capture stream cleaned up (display={self.display_index})")

    def _try_reinit(self) -> bool:
        """Attempt to reinitialize the DXGI capture after a failure.

        Returns True if reinit succeeded, False if on cooldown or failed.
        """
        now = time.monotonic()
        if now - self._last_reinit < _REINIT_COOLDOWN:
            return False
        self._last_reinit = now
        logger.warning(f"BetterCam: reinitializing camera for display {self.display_index}")
        try:
            self.cleanup()
            self.initialize()
            return True
        except Exception as reinit_err:
            logger.error(f"BetterCam reinit failed (display={self.display_index}): {reinit_err}")
            return False

    def capture_frame(self) -> Optional[ScreenCapture]:
        if not self._initialized:
            self.initialize()

        try:
            frame = self._camera.grab()

            if frame is None:
                return None

            logger.debug(
                f"BetterCam captured display {self.display_index}: "
                f"{frame.shape[1]}x{frame.shape[0]}"
            )

            return ScreenCapture(
                image=frame,
                width=frame.shape[1],
                height=frame.shape[0],
                display_index=self.display_index,
            )

        except ValueError:
            raise
        except Exception as e:
            logger.error(f"Failed to capture display {self.display_index} with BetterCam: {e}")
            # DXGI duplication can be lost after display changes / sleep / UAC.
            # Attempt to recreate the camera so capture can recover.
            if self._try_reinit():
                return None  # let the caller retry on next tick
            raise RuntimeError(f"Screen capture failed: {e}")


class BetterCamEngine(CaptureEngine):
    """BetterCam-based screen capture engine.

    Uses the bettercam library (a high-performance fork of DXCam) which leverages
    DXGI Desktop Duplication API for ultra-fast screen capture on Windows.

    Requires: Windows 8.1+
    """

    ENGINE_TYPE = "bettercam"
    ENGINE_PRIORITY = 4

    @classmethod
    def is_available(cls) -> bool:
        if sys.platform != "win32":
            return False
        try:
            import importlib.util
            return importlib.util.find_spec("bettercam") is not None
        except (ImportError, ModuleNotFoundError, ValueError):
            return False

    @classmethod
    def get_default_config(cls) -> Dict[str, Any]:
        return {}

    @classmethod
    def get_available_displays(cls) -> List[DisplayInfo]:
        try:
            import mss

            displays = []
            with mss.mss() as sct:
                for i, monitor in enumerate(sct.monitors[1:], start=0):
                    displays.append(DisplayInfo(
                        index=i,
                        name=f"Display {i}",
                        width=monitor["width"],
                        height=monitor["height"],
                        x=monitor["left"],
                        y=monitor["top"],
                        is_primary=(i == 0),
                        refresh_rate=60,
                    ))

            logger.debug(f"BetterCam detected {len(displays)} display(s)")
            return displays

        except Exception as e:
            logger.error(f"Failed to detect displays with BetterCam: {e}")
            raise RuntimeError(f"Failed to detect displays: {e}")

    @classmethod
    def create_stream(cls, display_index: int, config: Dict[str, Any]) -> BetterCamCaptureStream:
        return BetterCamCaptureStream(display_index, config)
