import time
import queue
import threading
import logging
import gc
import cv2
import numpy as np

from ..utils import check_rtsp_port

_logger = logging.getLogger(__name__)

class ConnectionManager:
    def __init__(self, backend, ffmpeg_settings=None, logger=None, candidate=False):
        """
        Handles camera connections, backend opening, and reconnection scheduling.
        
        Args:
            backend (str): 'ffmpeg', 'gst', or 'opencv'.
            ffmpeg_settings (dict): Settings for FFmpeg backend.
            logger (logging.Logger): Logger instance.
            candidate (bool): Whether to use candidate RTSP strings on failure.
        """
        self.backend = backend
        self.logger = logger or _logger
        self.candidate = candidate
        
        # FFmpeg specific settings
        ffmpeg_settings = ffmpeg_settings or {}
        self.ffmpeg_fps = ffmpeg_settings.get('fps', 5)
        self.ffmpeg_hwaccel = ffmpeg_settings.get('hwaccel', True)
        self.ffmpeg_fallback = ffmpeg_settings.get('fallback', True)
        self.ffmpeg_zero_copy = ffmpeg_settings.get('zero_copy', True)
        
        self.reconnect_queue = queue.Queue()
        self.running = True
        self.mutex = None
        self.scheduler_thread = None

        # --- Backend Strategy Initialization (Lazy Import at Init) ---
        if self.backend == 'ffmpeg':
            from ..backend.ffmpeg import FFmpegMultiBranchCapture
            self._cap_cls = FFmpegMultiBranchCapture
        elif self.backend == 'gst':
            from ..backend.gstreamer import GStreamerCapture
            self._cap_cls = GStreamerCapture
        else:
            from ..backend.opencv import OpenCVCapture
            self._cap_cls = OpenCVCapture

    def start_scheduler(self, mutex):
        """Starts the dedicated reconnection scheduler thread."""
        self.mutex = mutex
        self.scheduler_thread = threading.Thread(
            target=self._reconnect_scheduler, 
            name="ReconnectScheduler", 
            daemon=True
        )
        self.scheduler_thread.start()
        return self.scheduler_thread

    def stop(self):
        """Stops the scheduler."""
        self.running = False

    def init_camera(self, cam):
        """Initial connection attempt for a camera."""
        for rtsp in cam['rtsp_strings']:
            cam['cap'] = self.open_backend(rtsp, cam)
            if cam['cap'].isOpened():
                cam['status'] = True
                cam['rtsp_reconnect'] = rtsp
                break
            else:
                cam['status'] = False
                cam['rtsp_reconnect'] = None if self.candidate else rtsp

    def open_backend(self, rtsp, cam):
        """Opens the appropriate backend for the given RTSP URL."""
        # Ensure we have branch config (at least one)
        branches = cam.get('branches') or {1: {'WIDTH': 1920, 'HEIGHT': 1080, 'COLOR': 'BGR', 'LETTERBOX': False}}

        if self.backend == 'ffmpeg':
            if not rtsp.startswith('rtsp://'): 
                return type('Dummy', (), {'isOpened': lambda s: False, 'release': lambda s: None, 'get_branches': lambda: (False, None)})()
            if not check_rtsp_port(rtsp, self.logger): 
                return type('Dummy', (), {'isOpened': lambda s: False, 'release': lambda s: None, 'get_branches': lambda: (False, None)})()
            
            cap = self._cap_cls(
                branches,
                fps=self.ffmpeg_fps, 
                hwaccel=self.ffmpeg_hwaccel, 
                fallback=self.ffmpeg_fallback, 
                zero_copy=self.ffmpeg_zero_copy
            )
            cap.open(rtsp)
            return cap
        elif self.backend == 'gst':
            cap = self._cap_cls(rtsp, branches=branches, codec=cam.get('codec', 'h264'))
            cap.open()
            return cap
        else:
            # OpenCVCapture
            return self._cap_cls(rtsp, branches)

    def _perform_reconnect(self, cam, old_cap):
        """Internal logic to perform camera reconnection."""
        if old_cap: 
            old_cap.release()
        gc.collect()
        time.sleep(0.2)
        
        # Priority 1: Use last successful RTSP string
        if cam.get('rtsp_reconnect'):
            print('priority 1')
            return self.open_backend(cam['rtsp_reconnect'], cam)
        
        # Priority 2: Try all candidates
        for rtsp in cam.get('rtsp_strings', []):
            print('priority 2')
            new_cap = self.open_backend(rtsp, cam)
            if new_cap.isOpened():
                cam['rtsp_reconnect'] = rtsp
                return new_cap
            time.sleep(0.2)
        
        # Priority 3: Default to first string (or None)
        first_rtsp = cam['rtsp_strings'][0] if cam.get('rtsp_strings') else None
        print('priority 3')
        return self.open_backend(first_rtsp, cam) if first_rtsp else None

    def _reconnect_scheduler(self):
        """Dedicated worker that processes reconnections one by one."""
        self.logger.info("Reconnection Scheduler started.")
        while self.running:
            try:
                # Get camera from queue
                cam = self.reconnect_queue.get(timeout=1.0)
                self.logger.info(f"--- Scheduler: Starting reconnection for Cam {cam['_id']} ---")
                
                new_cap = self._perform_reconnect(cam, cam.get('cap'))
                
                with self.mutex:
                    cam['cap'] = new_cap
                    cam['pending_reconnect'] = False
                
                self.logger.info(f"--- Scheduler: Reconnection complete for Cam {cam['_id']} ---")
                self.reconnect_queue.task_done()
                time.sleep(0.2)
                
            except queue.Empty:
                continue
            except Exception as e:
                self.logger.error(f"Error in Reconnection Scheduler: {e}", exc_info=True)

    def request_reconnect(self, cam):
        """Submits a camera to the reconnection queue."""
        if not cam.get('pending_reconnect'):
            self.logger.info(f"Cam {cam['_id']}: Requesting reconnection from scheduler")
            cam['pending_reconnect'] = True
            self.reconnect_queue.put(cam)
