from __future__ import annotations

from typing import List

import numpy as np


def visualize_silhouette(cluster_lists: List[int], X_features):
    """여러 K 값에 대해 실루엣 플롯을 시각화합니다.

    Parameters
    ----------
    cluster_lists:
        평가할 클러스터 개수 리스트 (예: [2, 3, 4, 5])
    X_features:
        2D feature array (샘플 수 x feature 차원)

    Notes
    -----
    - 내부적으로 KMeans(random_state=0)를 사용해 재현성을 확보합니다.
    - 실루엣 평균선(빨간 점선)을 함께 표시합니다.
    """
    import matplotlib.cm as cm
    import matplotlib.pyplot as plt
    from sklearn.cluster import KMeans
    from sklearn.metrics import silhouette_samples, silhouette_score

    n_cols = len(cluster_lists)
    fig, axs = plt.subplots(figsize=(4 * n_cols, 4), nrows=1, ncols=n_cols)
    if n_cols == 1:
        axs = [axs]

    for ind, n_cluster in enumerate(cluster_lists):
        clusterer = KMeans(n_clusters=n_cluster, max_iter=500, random_state=0)
        cluster_labels = clusterer.fit_predict(X_features)

        sil_avg = silhouette_score(X_features, cluster_labels)
        sil_values = silhouette_samples(X_features, cluster_labels)

        y_lower = 10
        axs[ind].set_title(
            "클러스터 개수: "
            + str(n_cluster)
            + "\n"
            + "실루엣 점수: "
            + str(round(float(sil_avg), 3))
        )
        axs[ind].set_xlabel("실루엣 계수 값")
        axs[ind].set_ylabel("클러스터 라벨")
        axs[ind].set_xlim([-0.1, 1])
        axs[ind].set_ylim([0, len(X_features) + (n_cluster + 1) * 10])
        axs[ind].set_yticks([])
        axs[ind].set_xticks([0, 0.2, 0.4, 0.6, 0.8, 1])

        for i in range(n_cluster):
            ith_cluster_sil_values = sil_values[cluster_labels == i]
            ith_cluster_sil_values.sort()

            size_cluster_i = ith_cluster_sil_values.shape[0]
            y_upper = y_lower + size_cluster_i

            color = cm.nipy_spectral(float(i) / n_cluster)
            axs[ind].fill_betweenx(
                np.arange(y_lower, y_upper),
                0,
                ith_cluster_sil_values,
                facecolor=color,
                edgecolor=color,
                alpha=0.7,
            )
            axs[ind].text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
            y_lower = y_upper + 10

        axs[ind].axvline(x=sil_avg, color="red", linestyle="--")

    plt.show()
