import os
from typing import Optional, Dict, Any
from nxva.yolo.yolo import YOLOToolBox, YOLOOptions
from ..val_speed.resource_monitor import ResourceMonitor
from ..val_speed.plot import plot_resources

def run_evaluation(
    task: str = "detect",
    weights: str = "yolo11s.pt",
    val_set: str = "val.txt",
    iou: float = 0.6,
    conf: float = 0.001,
    size: int = 320,
    device: str = 'cpu',
    batch_size: int = 1,
    save_dir: Optional[str] = None,
    plots: bool = True,
    class_names: Optional[Dict[int, str]] = None,
    options: Optional[YOLOOptions] = None,
    verbose: bool = True,
    profile_resources: bool = False,
    **kwargs
):
    """
    General evaluation function for different YOLO tasks.
    
    Args:
        task (str): YOLO task type ('detect', 'segment', 'pose', 'classify').
        weights (str): Path to model weights file.
        val_set (str): Path to validation dataset (txt file listing images).
        iou (float): IoU threshold for evaluation.
        conf (float): Confidence threshold for evaluation.
        size (int): Input image size.
        device (str): Device to run evaluation on ('cpu', 'cuda', etc.).
        batch_size (int): Batch size for validation.
        save_dir (str): Directory to save results.
        plots (bool): Whether to save plots (PR curves, etc.).
        class_names (dict): Optional dictionary mapping class indices to names.
        options (YOLOOptions): Optional pre-configured YOLOOptions object.
        verbose (bool): Whether to print verbose output.
        **kwargs: Additional parameters for YOLOOptions.
    """
    print(f"\n--- Evaluating Task: {task} ---")
    
    if save_dir is None:
        save_dir = f"runs/val_{task}"

    # 1. Initialize options if not provided
    if options is None:
        options = YOLOOptions(
            task=task,
            weights=weights,
            size=size,
            device=device,
            conf=conf,
            iou=iou,
            class_names=class_names,
            **kwargs
        )
    
    try:
        model = YOLOToolBox(options)
        
        # 2. Run validation (pass parameters directly to model.val)
        monitor = None
        if profile_resources:
            monitor = ResourceMonitor(interval=1.0)
            monitor.start()

        metrics = model.val(
            val_txt_path=val_set,
            batch_size=batch_size,
            save_dir=save_dir,
            plots=plots,
            conf=conf,
            iou=iou,
            verbose=verbose
        )

        if monitor:
            res_data = monitor.stop()
            os.makedirs(save_dir, exist_ok=True)
            monitor.save_data(os.path.join(save_dir, 'resource_usage.csv'))
            plot_resources(res_data, output_dir=save_dir)
        
        # 3. Handle task-specific results
        print(f"\nResults for {task.upper()}:")
        
        if task == "detect":
            # Detection: Bbox metrics (suffix B)
            print(f" - Bbox mAP50:    {metrics.get('metrics/mAP50(B)', 0):.4f}")
            print(f" - Bbox mAP50-95: {metrics.get('metrics/mAP50-95(B)', 0):.4f}")
            
        elif task == "segment":
            # Segmentation: Bbox and Mask (suffix M) metrics
            print(f" - Bbox mAP50:    {metrics.get('metrics/mAP50(B)', 0):.4f}")
            print(f" - Mask mAP50:    {metrics.get('metrics/mAP50(M)', 0):.4f}")
            
        elif task == "pose":
            # Pose: Bbox and Keypoint (suffix P) metrics
            print(f" - Bbox mAP50:    {metrics.get('metrics/mAP50(B)', 0):.4f}")
            print(f" - Pose mAP50:    {metrics.get('metrics/mAP50(P)', 0):.4f}")
            
        elif task == "classify":
            # Classification: Accuracy metrics
            print(f" - Top-1 Acc:     {metrics.get('metrics/accuracy_top1', 0):.4f}")
            print(f" - Top-5 Acc:     {metrics.get('metrics/accuracy_top5', 0):.4f}")
            
        return metrics

    except Exception as e:
        print(f"Evaluation failed: {e}")
        import traceback
        traceback.print_exc()
        return None
