"""Tests for clustering — label generation, cosine similarity, result types, union-find."""

from __future__ import annotations

from arandu.background.clustering import (
    ClusteringResult,
    CommunityDetectionResult,
    _cluster_label_for_entity,
    _cosine_similarity,
    _UnionFind,
)

# ---------------------------------------------------------------------------
# Entity label mapping
# ---------------------------------------------------------------------------


def test_label_user_self():
    """user:self should map to 'User'."""
    assert _cluster_label_for_entity("user", "self") == "User"


def test_label_social_prefix():
    """social entity should use title-cased entity_key name."""
    assert _cluster_label_for_entity("social", "ana") == "Ana"


def test_label_fallback():
    """Unknown entity should use title-cased entity_key."""
    assert _cluster_label_for_entity("custom", "my_entity") == "My Entity"


def test_label_empty_key():
    """Empty entity_key should fallback to entity_type."""
    assert _cluster_label_for_entity("person", "") == "Person"


# ---------------------------------------------------------------------------
# Cosine similarity
# ---------------------------------------------------------------------------


def test_cosine_identical():
    """Identical vectors should have similarity 1.0."""
    v = [1.0, 0.0, 0.0]
    assert abs(_cosine_similarity(v, v) - 1.0) < 0.001


def test_cosine_orthogonal():
    """Orthogonal vectors should have similarity 0.0."""
    a = [1.0, 0.0]
    b = [0.0, 1.0]
    assert abs(_cosine_similarity(a, b)) < 0.001


def test_cosine_empty():
    """Empty vectors should return 0.0."""
    assert _cosine_similarity([], []) == 0.0


def test_cosine_different_lengths():
    """Different length vectors should return 0.0."""
    assert _cosine_similarity([1.0], [1.0, 2.0]) == 0.0


# ---------------------------------------------------------------------------
# Union-Find
# ---------------------------------------------------------------------------


def test_union_find_basic():
    """Union-Find should group connected elements."""
    uf = _UnionFind(4)
    uf.union(0, 1)
    uf.union(2, 3)
    groups = uf.groups()
    assert len(groups) == 2
    group_sizes = sorted(len(v) for v in groups.values())
    assert group_sizes == [2, 2]


def test_union_find_transitive():
    """Union-Find should handle transitive connections."""
    uf = _UnionFind(3)
    uf.union(0, 1)
    uf.union(1, 2)
    groups = uf.groups()
    assert len(groups) == 1
    assert len(list(groups.values())[0]) == 3


def test_union_find_no_unions():
    """Without unions, each element is its own group."""
    uf = _UnionFind(3)
    groups = uf.groups()
    assert len(groups) == 3


# ---------------------------------------------------------------------------
# Result dataclasses
# ---------------------------------------------------------------------------


def test_clustering_result_defaults():
    """ClusteringResult should have sane defaults."""
    r = ClusteringResult()
    assert r.clusters_created == 0
    assert r.clusters_reinforced == 0
    assert r.summaries_generated == 0
    assert r.facts_assigned == 0


def test_community_detection_result_defaults():
    """CommunityDetectionResult should have sane defaults."""
    r = CommunityDetectionResult()
    assert r.communities_created == 0
    assert r.communities_reinforced == 0
    assert r.clusters_in_communities == 0
    assert r.skipped is False
    assert r.skip_reason is None
