"""
DNA Fingerprint — архитектурный «отпечаток» проекта.

Агрегирует паттерны, хабы, языки, сложность и структуру слоёв
в единый объект ProjectDNA, который можно сравнивать и отслеживать.
"""

from __future__ import annotations

import time
from collections import Counter
from dataclasses import dataclass, field
from pathlib import Path

from ..memory.store import KodaraIndex
from .patterns import detect_patterns


@dataclass
class ProjectDNA:
    project_name: str
    detected_patterns: dict[str, list[str]]   # category → list[pattern_name]
    hub_modules: list[str]                     # топ-5 по in-degree
    language_profile: dict[str, int]           # language → file_count
    complexity_score: float                    # средних зависимостей на модуль
    layer_structure: list[str]                 # уникальные top-level директории
    created_at: float = field(default_factory=time.time)


def build_fingerprint(index: KodaraIndex) -> ProjectDNA:
    """
    Строит архитектурный fingerprint из KodaraIndex.

    - detected_patterns: результат detect_patterns()
    - hub_modules:       топ-5 по in-degree (считается из graph_edges)
    - language_profile:  index.language_counts
    - complexity_score:  len(graph_edges) / len(modules), или 0.0
    - layer_structure:   уникальные top-level части путей файлов
    """
    # Паттерны
    patterns = detect_patterns(index)

    # Hub modules — считаем in-degree из graph_edges
    in_degree: Counter = Counter()
    for edge in index.graph_edges:
        target = edge.get("to", "")
        if target:
            in_degree[target] += 1

    hub_modules = [mod for mod, _ in in_degree.most_common(5)]

    # Языки
    language_profile = dict(index.language_counts) if index.language_counts else {}

    # Сложность
    num_modules = len(index.modules)
    complexity_score = (
        len(index.graph_edges) / num_modules if num_modules > 0 else 0.0
    )

    # Слои — уникальные top-level директории из путей файлов
    source_root = Path(index.source_root) if index.source_root else None
    layers: list[str] = []
    seen_layers: set[str] = set()

    for path_str in index.modules:
        path = Path(path_str)
        # Если есть source_root — берём относительный путь
        try:
            if source_root:
                rel = path.relative_to(source_root)
                parts = rel.parts
            else:
                parts = path.parts
        except ValueError:
            parts = path.parts

        # Первая часть (директория) — слой
        if len(parts) > 1:
            layer = parts[0]
            if layer not in seen_layers:
                seen_layers.add(layer)
                layers.append(layer)

    return ProjectDNA(
        project_name=index.display_name,
        detected_patterns=patterns,
        hub_modules=hub_modules,
        language_profile=language_profile,
        complexity_score=round(complexity_score, 2),
        layer_structure=sorted(layers),
    )
