from __future__ import annotations

from typing import Any, List


def agglomerative_silhouette(
    df: Any,
    vector_col: str = "vector",
    linkage: str = "ward",
    n_cluster_range: range = range(2, 10),
    plot: bool = True,
):
    """AgglomerativeClustering의 클러스터 개수별 실루엣 점수를 계산합니다.

    Parameters
    ----------
    df:
        pandas DataFrame(또는 유사 객체). `df[vector_col]`에는
        벡터(리스트/np.ndarray)가 문서 단위로 들어있어야 합니다.
    vector_col:
        벡터가 들어있는 컬럼명
    linkage:
        계층적 군집의 linkage 방식(예: 'ward', 'complete', 'average', 'single')
    n_cluster_range:
        평가할 클러스터 개수 범위(range)
    plot:
        True이면 (클러스터 개수, 실루엣 점수) 선 그래프를 그립니다.

    Returns
    -------
    scores:
        n_cluster_range 순서대로의 실루엣 평균 점수 리스트
    best_n:
        실루엣 평균 점수가 최대가 되는 클러스터 개수
    """
    from sklearn.cluster import AgglomerativeClustering
    from sklearn.metrics import silhouette_score
    import matplotlib.pyplot as plt

    X = list(df[vector_col])

    scores: List[float] = []
    for n in list(n_cluster_range):
        cluster = AgglomerativeClustering(n_clusters=n, linkage=linkage)
        labels = cluster.fit_predict(X)
        scores.append(float(silhouette_score(X, labels)))

    best_idx = int(max(range(len(scores)), key=lambda i: scores[i]))
    best_n = list(n_cluster_range)[best_idx]

    if plot:
        plt.plot(list(n_cluster_range), scores)
        plt.xlabel("클러스터 개수 (Number of Clusters)")
        plt.ylabel("실루엣 점수 (Silhouette Score)")
        plt.show()

    return scores, best_n
