"""CRX Refined — Cluster-then-infer CRX with over-approximation detection."""

from collections import defaultdict
from .crx import CRX
from .grammar import Epsilon


def _cluster_by_structure(sequences):
    """Cluster sequences by structural features."""
    groups = defaultdict(list)
    for seq in sequences:
        if not seq:
            continue
        length = len(seq)
        if length <= 3:
            length_bucket = 'short'
        elif length <= 8:
            length_bucket = 'med'
        else:
            length_bucket = 'long'
        key = (seq[0], seq[-1], length_bucket)
        groups[key].append(seq)
    return dict(groups)


def crx_refined(sequences, min_cluster=2):
    """Cluster-then-infer CRX."""
    S = [list(s) for s in sequences if s]
    if not S:
        return Epsilon()
    clusters = _cluster_by_structure(S)
    valid_clusters = {k: v for k, v in clusters.items() if len(v) >= min_cluster}
    if not valid_clusters:
        return CRX().infer(S)
    best_key = max(valid_clusters, key=lambda k: len(valid_clusters[k]))
    best_cluster = valid_clusters[best_key]
    return CRX().infer(best_cluster)


def crx_with_confidence(sequences, min_cluster=2):
    """Cluster-then-infer CRX with confidence metrics."""
    S = [list(s) for s in sequences if s]
    if not S:
        return {
            'grammar': Epsilon(),
            'confidence': 1.0,
            'n_clusters': 0,
            'largest_cluster': 0,
            'n_sequences': 0,
        }
    clusters = _cluster_by_structure(S)
    valid_clusters = {k: v for k, v in clusters.items() if len(v) >= min_cluster}
    if not valid_clusters:
        g = CRX().infer(S)
        return {
            'grammar': g,
            'confidence': 0.5,
            'n_clusters': len(clusters),
            'largest_cluster': max((len(v) for v in clusters.values()), default=0),
            'n_sequences': len(S),
        }
    best_key = max(valid_clusters, key=lambda k: len(valid_clusters[k]))
    best_cluster = valid_clusters[best_key]
    g = CRX().infer(best_cluster)
    captured = sum(1 for s in S if any(_matches_cluster(g, s) for c in valid_clusters.values() for s in c))
    confidence = captured / max(len(S), 1)
    return {
        'grammar': g,
        'confidence': round(confidence, 3),
        'n_clusters': len(valid_clusters),
        'largest_cluster': len(best_cluster),
        'n_sequences': len(S),
    }


def _matches_cluster(grammar, seq):
    from .grammar import match as grammar_match
    try:
        return grammar_match(grammar, seq)
    except Exception:
        return False
