import numpy as np
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from typing import Dict, Any, Optional

from ..utility_evaluator import UtilityEvaluator


class ClusteringSilhouette(UtilityEvaluator):
    """
    聚类轮廓系数效用评估器。
    对原始数据和扰动数据分别进行 KMeans 聚类，计算轮廓系数，然后比较。
    效用定义为两个轮廓系数的相似度（1 - 绝对差），值越大表示聚类结构越相似。

    参数:
        n_clusters: int，聚类数（默认 3）
        random_state: int，随机种子（默认 42）
    """

    def __init__(self, n_clusters: int = 3, random_state: int = 42):
        self.n_clusters = n_clusters
        self.random_state = random_state

    def evaluate(self,
                 original: np.ndarray,
                 perturbed: np.ndarray,
                 task: Optional[Dict[str, Any]] = None) -> float:
        """
        计算聚类结构相似度。

        Parameters
        ----------
        original : np.ndarray
            原始数据。
        perturbed : np.ndarray
            扰动后的数据。
        task : dict, optional
            可覆盖 n_clusters。

        Returns
        -------
        float
            效用值，范围 [0,1]，值越大表示聚类结构越相似。
        """
        n_clusters = self.n_clusters
        if task and 'n_clusters' in task:
            n_clusters = task['n_clusters']

        # 确保样本数不少于聚类数
        n_samples = min(original.shape[0], perturbed.shape[0])
        if n_samples < n_clusters:
            return 0.0  # 无法聚类

        # 对原始数据聚类
        kmeans_orig = KMeans(n_clusters=n_clusters, random_state=self.random_state, n_init=10)
        labels_orig = kmeans_orig.fit_predict(original)
        sil_orig = silhouette_score(original, labels_orig)

        # 对扰动数据聚类
        kmeans_pert = KMeans(n_clusters=n_clusters, random_state=self.random_state, n_init=10)
        labels_pert = kmeans_pert.fit_predict(perturbed)
        sil_pert = silhouette_score(perturbed, labels_pert)

        # 效用定义为轮廓系数的接近程度（1 - 绝对差）
        # 轮廓系数范围 [-1,1]，归一化差到 [0,1]
        diff = abs(sil_orig - sil_pert)
        utility = 1 - diff  # 因为 diff 最大可能为2，但实际中很少，暂不严格归一化
        utility = max(0.0, min(1.0, utility))  # 限制在 [0,1]

        return utility