import time
import psutil
import threading

class PerformanceMonitor:
    def __init__(self):
        self._start_time = None
        self._last_frame_time = 0.0
        self._fps = 0.0
        self._frame_count = 0
        self._fps_last_time = time.time()
        self._process = psutil.Process()
        self._cpu_usage = 0.0
        self._cpu_lock = threading.Lock()
        
        # Input lag tracking
        self._last_input_time = None
        self._input_lag_ms = 0.0
        
        # Alerts
        self.alert_callback = None
        self.thresholds = {
            'frame_time_ms': 33.3,  # < 30 FPS
            'cpu_percent': 80.0,
            'input_lag_ms': 100.0
        }
        self._last_alert_time = 0
        self._alert_cooldown = 30  # seconds

        # Detailed profiling
        self._component_timings = {}
        self._comp_start = {}
        
        # Start CPU usage monitoring thread
        self._stop_event = threading.Event()
        self._cpu_thread = threading.Thread(target=self._monitor_cpu, daemon=True)
        self._cpu_thread.start()

    def _monitor_cpu(self):
        while not self._stop_event.is_set():
            # psutil.cpu_percent is better than process.cpu_percent for real-time
            usage = self._process.cpu_percent(interval=1.0)
            with self._cpu_lock:
                self._cpu_usage = usage
            time.sleep(0.1)

    def start_frame(self):
        self._start_time = time.time()

    def record_input_event(self):
        self._last_input_time = time.time()

    def start_component(self, name: str):
        self._comp_start[name] = time.time()

    def end_component(self, name: str):
        if name in self._comp_start:
            elapsed = (time.time() - self._comp_start[name]) * 1000.0
            self._component_timings[name] = elapsed

    def end_frame(self):
        if self._start_time is None:
            return
        
        end_time = time.time()
        self._last_frame_time = (end_time - self._start_time) * 1000.0
        self._frame_count += 1
        
        # Calculate input lag if an input occurred during this frame
        if self._last_input_time is not None:
            self._input_lag_ms = (end_time - self._last_input_time) * 1000.0
            self._last_input_time = None

        self._check_alerts()
        
        elapsed_since_fps = end_time - self._fps_last_time
        if elapsed_since_fps >= 1.0:
            self._fps = self._frame_count / elapsed_since_fps
            self._frame_count = 0
            self._fps_last_time = end_time

    def _check_alerts(self):
        if not self.alert_callback:
            return
        
        now = time.time()
        if now - self._last_alert_time < self._alert_cooldown:
            return

        metrics = self.get_metrics()
        alerts = []
        if metrics['last_frame_time_ms'] > self.thresholds['frame_time_ms']:
            alerts.append(f"Frame time high: {metrics['last_frame_time_ms']:.1f}ms")
        if metrics['cpu_percent'] > self.thresholds['cpu_percent']:
            alerts.append(f"CPU usage high: {metrics['cpu_percent']:.1f}%")
        if metrics['input_lag_ms'] > self.thresholds['input_lag_ms']:
            alerts.append(f"Input lag high: {metrics['input_lag_ms']:.1f}ms")

        if alerts:
            self._last_alert_time = now
            self.alert_callback("; ".join(alerts))

    def get_metrics(self):
        with self._cpu_lock:
            cpu_usage = self._cpu_usage
            
        metrics = {
            'last_frame_time_ms': self._last_frame_time,
            'fps': self._fps,
            'cpu_percent': cpu_usage,
            'input_lag_ms': self._last_input_time if self._last_input_time else 0.0 # Wait, this should be the calculated lag
        }
        # Oops, fixed the input lag logic in previous turn, let's keep it consistent
        metrics['input_lag_ms'] = self._input_lag_ms
        
        # Add detailed timings
        for name, elapsed in self._component_timings.items():
            metrics[f'time_{name}_ms'] = elapsed
            
        return metrics

    def stop(self):
        self._stop_event.set()
        self._cpu_thread.join(timeout=2.0)
