"""
Metrics template for standard evaluation metrics.
Chuẩn POST lên DB: Accuracy, Precision, Recall, F1-Score, Confusion Matrix (bắt buộc).
"""
from typing import Dict, Any, List, Optional
import numpy as np

# Chuẩn bắt buộc khi POST /api/v1/metrics (trùng kwslib.standards.metrics)
STANDARD_METRIC_KEYS = ["accuracy", "precision", "recall", "f1_score", "confusion_matrix"]


class MetricsTemplate:
    """Template for standard evaluation metrics"""
    
    @staticmethod
    def create_empty() -> Dict[str, Any]:
        """
        Create empty metrics template with all standard fields.
        Chuẩn bắt buộc: accuracy, precision, recall, f1_score, confusion_matrix.
        Returns:
            Dictionary with all metric fields initialized to None
        """
        return {
            "accuracy": None,
            "precision": None,
            "recall": None,
            "f1_score": None,
            "confusion_matrix": None,  # 2D list int - bắt buộc khi POST
            "auc_macro": None,
            "auc_micro": None,
            "ap_macro": None,
            "ap_micro": None,
            "roc_curve": None,
            "pr_curve": None,
        }
    
    @staticmethod
    def format_roc_curve(fpr: np.ndarray, tpr: np.ndarray) -> List[Dict[str, float]]:
        """
        Format ROC curve data for API.
        
        Args:
            fpr: False Positive Rate array
            tpr: True Positive Rate array
        
        Returns:
            List of {fpr, tpr} dictionaries
        """
        return [{"fpr": float(f), "tpr": float(t)} for f, t in zip(fpr, tpr)]
    
    @staticmethod
    def format_pr_curve(recall: np.ndarray, precision: np.ndarray) -> List[Dict[str, float]]:
        """
        Format Precision-Recall curve data for API.
        
        Args:
            recall: Recall array
            precision: Precision array
        
        Returns:
            List of {recall, precision} dictionaries
        """
        return [{"recall": float(r), "precision": float(p)} for r, p in zip(recall, precision)]
    
    @staticmethod
    def format_confusion_matrix(cm: np.ndarray) -> List[List[int]]:
        """
        Format confusion matrix for API.
        
        Args:
            cm: Confusion matrix numpy array
        
        Returns:
            2D list of integers
        """
        return cm.tolist()
    
    @staticmethod
    def validate(metrics: Dict[str, Any], require_confusion_matrix: bool = True) -> bool:
        """
        Validate metrics dictionary.
        Chuẩn: accuracy, precision, recall, f1_score (0-1), confusion_matrix (2D list).

        Args:
            metrics: Metrics dictionary
            require_confusion_matrix: Nếu True thì confusion_matrix bắt buộc có (có thể None nếu chưa tính)

        Returns:
            True if valid, False otherwise
        """
        for field in STANDARD_METRIC_KEYS:
            if field not in metrics:
                return False
            if field == "confusion_matrix":
                if require_confusion_matrix and metrics.get(field) is None:
                    return False
                val = metrics.get(field)
                if val is not None and not isinstance(val, (list, np.ndarray)):
                    return False
                continue
            if metrics[field] is not None:
                if not (0 <= float(metrics[field]) <= 1):
                    return False
        optional = ["auc_macro", "auc_micro", "ap_macro", "ap_micro"]
        for field in optional:
            if field in metrics and metrics[field] is not None:
                if not (0 <= float(metrics[field]) <= 1):
                    return False
        return True

    @staticmethod
    def build_post_payload(
        model_id: int,
        dataset_split_id: int,
        experiment_run_id: int,
        metrics: Dict[str, Any],
    ) -> Dict[str, Any]:
        """
        Tạo payload chuẩn để POST /api/v1/metrics.
        Bắt buộc: accuracy, precision, recall, f1_score, confusion_matrix (2D list int).

        Args:
            model_id: ID model
            dataset_split_id: ID dataset split
            experiment_run_id: ID experiment run
            metrics: Dict từ create_empty() + đã điền giá trị

        Returns:
            Dict để gửi client.metrics.create(**payload) hoặc POST /metrics/
        """
        try:
            from kwslib.standards.metrics import build_metrics_payload
            return build_metrics_payload(
                model_id=model_id,
                dataset_split_id=dataset_split_id,
                experiment_run_id=experiment_run_id,
                metrics=metrics,
                require_standard=True,
            )
        except ImportError:
            pass
        payload = {
            "model_id": model_id,
            "dataset_split_id": dataset_split_id,
            "experiment_run_id": experiment_run_id,
            "accuracy": float(metrics.get("accuracy", 0)),
            "precision": float(metrics.get("precision", 0)),
            "recall": float(metrics.get("recall", 0)),
            "f1_score": float(metrics.get("f1_score", 0)),
            "confusion_matrix": (
                np.asarray(metrics["confusion_matrix"]).astype(int).tolist()
                if metrics.get("confusion_matrix") is not None
                else None
            ),
        }
        for key in ["auc_macro", "auc_micro", "ap_macro", "ap_micro"]:
            if metrics.get(key) is not None:
                payload[key] = float(metrics[key])
        for key in ["roc_curve", "pr_curve"]:
            if metrics.get(key) is not None:
                payload[key] = metrics[key]
        return payload
