import os
from PIL import Image
from screeninfo import get_monitors
from ws_package.log.writer import LogWriter
from ws_package.monitoring.screenshots import (
    CaptureMode, apply_filter, convert_image,
)
from ws_package.utils.screenshot_naming import make_screenshot_filename
from ws_package.utils.system import resolve_screenshot_directory

def mss():
    from mss import mss as _mss
    return _mss()

logger = LogWriter.file(__name__)


class ScreenShot:
    def __init__(
        self,
        dir: str,
        format: str = "JPEG",
        prefix: str = "_",
        quality: int = 100,
        blur_intensity: int = 1,
        ssfrequencystatus: int = 1,
        stop_event=None
    ):
        self.format = format
        # Normalize relative paths (production cwd is often non-writable _internal/).
        self.dir = resolve_screenshot_directory(dir) if not os.path.isabs(dir) else dir
        self.prefix = prefix
        self.quality = quality
        self.blur_intensity = blur_intensity
        self.ssfrequencystatus = ssfrequencystatus
        self.results = []
        self._executed = False  # For optional `.get()` support
        self.stop_event = stop_event

    def screenshot(self):
        screenshots = []
        monitors = get_monitors()

        with mss() as sct:
            for i, monitor in enumerate(monitors):
                logger.info("Windows screen %s: x=%s, y=%s, width=%s, height=%s", i + 1, monitor.x, monitor.y, monitor.width, monitor.height)
                bbox = {
                    "left": monitor.x,
                    "top": monitor.y,
                    "width": monitor.width,
                    "height": monitor.height
                }
                try:
                    shot = sct.grab(bbox)
                    img = Image.frombytes("RGB", shot.size, shot.rgb)
                    screenshots.append((img, monitor))
                except Exception as e:
                    logger.error("Error capturing monitor %s: %s", i + 1, e)
                    # FIXED: Cleanup image if it was created but append failed
                    if 'img' in locals():
                        try:
                            img.close()
                        except Exception:
                            pass

        return screenshots

    def save(self):
        results = []
        
        # First, get the number of monitors to calculate total target
        initial_screenshots = self.screenshot()
        num_monitors = len(initial_screenshots)
        # Close the initial screenshots as we just needed the count
        for screenshot, _ in initial_screenshots:
            try:
                screenshot.close()
            except Exception:
                pass
        
        # Calculate total target: ssfrequencystatus screenshots per monitor
        target_count = self.ssfrequencystatus * num_monitors
        screenshot_count = 0
        # Track screenshots per monitor to ensure each gets exactly ssfrequencystatus
        monitor_counts = {i: 0 for i in range(num_monitors)}
        
        logger.info("[SCREENSHOT] Capture plan: %s monitor(s), %s screenshot(s) per monitor = %s total screenshot(s)", num_monitors, self.ssfrequencystatus, target_count)

        # Capture screenshots until each monitor has ssfrequencystatus screenshots
        while screenshot_count < target_count:
            if self.stop_event and self.stop_event.is_set():
                break
            screenshots = self.screenshot()
            last_processed_idx = -1

            for i, (screenshot, monitor) in enumerate(screenshots):
                if self.stop_event and self.stop_event.is_set():
                    # Close remaining screenshots before breaking
                    for remaining_idx in range(i, len(screenshots)):
                        try:
                            screenshots[remaining_idx][0].close()
                        except Exception:
                            pass
                    break
                
                # Check if this monitor has already reached its target
                if monitor_counts[i] >= self.ssfrequencystatus:
                    # This monitor already has enough screenshots, close and skip
                    logger.debug("[SCREENSHOT] Monitor %s already has %s/%s screenshots, skipping", i+1, monitor_counts[i], self.ssfrequencystatus)
                    screenshot.close()
                    continue
                
                if screenshot_count >= target_count:
                    # Close unused screenshots if we've reached total target
                    screenshot.close()
                    last_processed_idx = i
                    break
                
                original_screenshot = screenshot  # Keep reference for cleanup
                processed_screenshot = None
                
                try:
                    if self.blur_intensity == 1:
                        blurred = apply_filter(screenshot, 5)
                        # FIXED: Close original if new image created
                        if blurred is not screenshot:
                            screenshot.close()
                        screenshot = blurred
                        processed_screenshot = screenshot

                    converted = convert_image(screenshot, self.format)
                    # FIXED: Close previous if new image created
                    if converted is not screenshot:
                        screenshot.close()
                    screenshot = converted
                    processed_screenshot = screenshot

                    filename = make_screenshot_filename(
                        self.format, offset_seconds=screenshot_count
                    )
                    path = os.path.join(self.dir, f"{self.prefix}{filename}")

                    screenshot.save(path, format=self.format, quality=self.quality)
                    results.append((screenshot, path, filename))
                    screenshot_count += 1
                    monitor_counts[i] += 1
                    last_processed_idx = i
                    logger.debug("[SCREENSHOT] Saved screenshot %s/%s for monitor %s (monitor %s has %s/%s)", screenshot_count, target_count, i+1, i+1, monitor_counts[i], self.ssfrequencystatus)
                except Exception as e:
                    # FIXED: Ensure cleanup on error
                    logger.error("Error processing screenshot: %s", e)
                    if processed_screenshot is not None and processed_screenshot is not original_screenshot:
                        try:
                            processed_screenshot.close()
                        except Exception:
                            pass
                    if original_screenshot is not None and original_screenshot is not processed_screenshot:
                        try:
                            original_screenshot.close()
                        except Exception:
                            pass
                    raise
            
            # Close any remaining unused screenshots from this capture
            if screenshot_count >= target_count and last_processed_idx >= 0:
                for remaining_idx in range(last_processed_idx + 1, len(screenshots)):
                    if remaining_idx < len(screenshots):
                        try:
                            screenshots[remaining_idx][0].close()
                        except Exception:
                            pass
        
        per_monitor = ", ".join(f"Monitor {i+1}: {monitor_counts.get(i, 0)}" for i in range(num_monitors))
        logger.info("[SCREENSHOT] Capture complete: %s total screenshot(s). Per monitor: %s", screenshot_count, per_monitor)
        return results

    def __enter__(self):
        if not os.path.exists(self.dir):
            os.makedirs(self.dir)
        self.results = self.save()
        self._executed = True
        return self.results

    def __exit__(self, exc_type, exc_val, exc_tb):
        for result in self.results:
            if result[0] is not None:
                result[0].close()

    def get(self):
        """Allow calling outside a 'with' block."""
        if not self._executed:
            try:
                if not os.path.exists(self.dir):
                    os.makedirs(self.dir)
                self.results = self.save()
                self._executed = True
            except Exception as e:
                # FIXED: Cleanup on error to prevent memory leaks
                if hasattr(self, 'results') and self.results:
                    for result in self.results:
                        if result and len(result) > 0 and result[0] is not None:
                            try:
                                result[0].close()
                            except Exception:
                                pass
                raise
        return self.results