"""
DNA Engine — тесты.

Покрывают:
  - detect_patterns: обнаружение JWT, Repository, пустой индекс
  - build_fingerprint: complexity_score, hub_modules, layer_structure
  - get_evolution: нет snapshots → пустой список
  - regression: импорт build_fingerprint работает
"""

from __future__ import annotations

import time
from pathlib import Path

import pytest

from kodara.memory.store import KodaraIndex


# ── Вспомогательная фабрика индексов ─────────────────────────────────────────

def _make_index(
    modules: dict | None = None,
    graph_edges: list | None = None,
    language_counts: dict | None = None,
    source_root: str = "/project",
    display_name: str = "test-project",
) -> KodaraIndex:
    return KodaraIndex(
        repo_id="local/test-abc12345",
        display_name=display_name,
        source_root=source_root,
        indexed_at=time.time(),
        index_version=3,
        file_count=len(modules or {}),
        language_counts=language_counts or {"python": len(modules or {})},
        modules=modules or {},
        graph_edges=graph_edges or [],
        file_hashes={},
    )


def _mod(path: str, classes: list | None = None, functions: list | None = None) -> dict:
    return {
        "path": path,
        "module_name": Path(path).stem,
        "language": "python",
        "classes": classes or [],
        "functions": functions or [],
        "exports": [],
        "imports": [],
        "file_hash": "abc",
        "summary": "",
        "dependencies": [],
        "dependents": [],
        "git": {},
    }


# ── detect_patterns ───────────────────────────────────────────────────────────

def test_detect_patterns_jwt():
    """Класс JWTManager → Auth: JWT обнаружен."""
    from kodara.dna.patterns import detect_patterns

    idx = _make_index(modules={
        "/project/auth/jwt_manager.py": _mod(
            "/project/auth/jwt_manager.py",
            classes=["JWTManager"],
            functions=["create_token", "verify_token"],
        )
    })
    result = detect_patterns(idx)
    assert "JWT" in result.get("Auth", []), f"Ожидался JWT в Auth, получил: {result}"


def test_detect_patterns_repository():
    """Класс UserRepository → Database: Repository обнаружен."""
    from kodara.dna.patterns import detect_patterns

    idx = _make_index(modules={
        "/project/db/user_repository.py": _mod(
            "/project/db/user_repository.py",
            classes=["UserRepository"],
        )
    })
    result = detect_patterns(idx)
    assert "Repository" in result.get("Database", []), f"Ожидался Repository в Database, получил: {result}"


def test_detect_patterns_empty_index():
    """Пустой индекс не падает, возвращает все категории с пустыми списками."""
    from kodara.dna.patterns import detect_patterns, PATTERNS

    idx = _make_index(modules={})
    result = detect_patterns(idx)

    # Все категории присутствуют
    expected_categories = set(p.category for p in PATTERNS)
    for cat in expected_categories:
        assert cat in result, f"Категория {cat!r} отсутствует в результате"

    # Все значения — пустые списки
    for cat, names in result.items():
        assert names == [], f"Категория {cat!r} должна быть пустой, но содержит: {names}"


def test_detect_patterns_multiple_categories():
    """Индекс с несколькими паттернами — все категории обнаружены корректно."""
    from kodara.dna.patterns import detect_patterns

    idx = _make_index(modules={
        "/project/auth/jwt.py": _mod("/project/auth/jwt.py", classes=["JWTAuth"]),
        "/project/db/repo.py": _mod("/project/db/repo.py", classes=["UserRepository"]),
        "/project/cache/redis_cache.py": _mod("/project/cache/redis_cache.py", functions=["redis_get"]),
    })
    result = detect_patterns(idx)
    assert "JWT" in result.get("Auth", [])
    assert "Repository" in result.get("Database", [])
    assert "Redis" in result.get("Caching", [])


# ── build_fingerprint ─────────────────────────────────────────────────────────

def test_build_fingerprint_complexity_score():
    """complexity_score = edges / modules."""
    from kodara.dna.fingerprint import build_fingerprint

    modules = {
        "/project/a.py": _mod("/project/a.py"),
        "/project/b.py": _mod("/project/b.py"),
        "/project/c.py": _mod("/project/c.py"),
        "/project/d.py": _mod("/project/d.py"),
    }
    edges = [
        {"from": "/project/a.py", "to": "/project/b.py", "kind": "import"},
        {"from": "/project/b.py", "to": "/project/c.py", "kind": "import"},
    ]
    idx = _make_index(modules=modules, graph_edges=edges)
    dna = build_fingerprint(idx)

    expected = round(2 / 4, 2)
    assert dna.complexity_score == expected, f"Ожидалось {expected}, получил {dna.complexity_score}"


def test_build_fingerprint_complexity_zero_modules():
    """Пустой индекс — complexity_score = 0.0, не падает."""
    from kodara.dna.fingerprint import build_fingerprint

    idx = _make_index(modules={}, graph_edges=[])
    dna = build_fingerprint(idx)
    assert dna.complexity_score == 0.0


def test_build_fingerprint_hub_modules_not_empty():
    """Если есть edges — hub_modules непустой."""
    from kodara.dna.fingerprint import build_fingerprint

    modules = {f"/project/mod{i}.py": _mod(f"/project/mod{i}.py") for i in range(5)}
    # mod0 — самый популярный (4 входящих ребра)
    edges = [
        {"from": f"/project/mod{i}.py", "to": "/project/mod0.py", "kind": "import"}
        for i in range(1, 5)
    ]
    idx = _make_index(modules=modules, graph_edges=edges)
    dna = build_fingerprint(idx)

    assert len(dna.hub_modules) > 0, "hub_modules должен быть непустым"
    assert "/project/mod0.py" in dna.hub_modules, "mod0.py должен быть в hub_modules"


def test_build_fingerprint_layer_structure():
    """layer_structure определяется из top-level директорий путей файлов."""
    from kodara.dna.fingerprint import build_fingerprint

    modules = {
        "/project/api/server.py": _mod("/project/api/server.py"),
        "/project/api/routes.py": _mod("/project/api/routes.py"),
        "/project/models/user.py": _mod("/project/models/user.py"),
        "/project/utils/helpers.py": _mod("/project/utils/helpers.py"),
    }
    idx = _make_index(modules=modules, source_root="/project")
    dna = build_fingerprint(idx)

    assert "api" in dna.layer_structure, f"api должен быть в layers: {dna.layer_structure}"
    assert "models" in dna.layer_structure
    assert "utils" in dna.layer_structure
    # Дубликаты отсутствуют
    assert len(dna.layer_structure) == len(set(dna.layer_structure))


def test_build_fingerprint_project_name():
    """project_name берётся из display_name индекса."""
    from kodara.dna.fingerprint import build_fingerprint

    idx = _make_index(display_name="my-saas")
    dna = build_fingerprint(idx)
    assert dna.project_name == "my-saas"


# ── get_evolution ─────────────────────────────────────────────────────────────

def test_get_evolution_no_snapshots(tmp_path):
    """Нет snapshots → get_evolution возвращает []."""
    from kodara.dna.evolution import get_evolution

    result = get_evolution(tmp_path)
    assert result == []


def test_get_evolution_nonexistent_root(tmp_path):
    """Несуществующий .kodara → graceful, возвращает []."""
    from kodara.dna.evolution import get_evolution

    fake_root = tmp_path / "no_such_project"
    result = get_evolution(fake_root)
    assert result == []


# ── Regression: импорт ────────────────────────────────────────────────────────

def test_import_build_fingerprint():
    """Регрессия: from kodara.dna.fingerprint import build_fingerprint работает."""
    from kodara.dna.fingerprint import build_fingerprint
    assert callable(build_fingerprint)


def test_import_detect_patterns():
    """Регрессия: from kodara.dna.patterns import detect_patterns работает."""
    from kodara.dna.patterns import detect_patterns, PATTERNS
    assert callable(detect_patterns)
    assert len(PATTERNS) > 0


def test_import_get_evolution():
    """Регрессия: from kodara.dna.evolution import get_evolution работает."""
    from kodara.dna.evolution import get_evolution
    assert callable(get_evolution)


def test_import_dna_module():
    """Регрессия: import kodara.dna работает."""
    import kodara.dna
    assert kodara.dna is not None


# ── Сравнение двух проектов (unit-level) ──────────────────────────────────────

def test_compare_two_dna_objects():
    """Сравнение DNA двух разных проектов — общие и уникальные паттерны."""
    from kodara.dna.fingerprint import build_fingerprint

    idx1 = _make_index(
        display_name="backend",
        modules={
            "/p1/auth/jwt.py": _mod("/p1/auth/jwt.py", classes=["JWTManager"]),
            "/p1/db/repo.py": _mod("/p1/db/repo.py", classes=["UserRepository"]),
        },
        source_root="/p1",
    )
    idx2 = _make_index(
        display_name="frontend",
        modules={
            "/p2/auth/session.py": _mod("/p2/auth/session.py", classes=["SessionManager"]),
            "/p2/db/repo.py": _mod("/p2/db/repo.py", classes=["PostRepository"]),
        },
        source_root="/p2",
    )

    dna1 = build_fingerprint(idx1)
    dna2 = build_fingerprint(idx2)

    patterns1 = {n for names in dna1.detected_patterns.values() for n in names}
    patterns2 = {n for names in dna2.detected_patterns.values() for n in names}

    common = patterns1 & patterns2
    only1 = patterns1 - patterns2
    only2 = patterns2 - patterns1

    # Repository есть в обоих
    assert "Repository" in common, f"Repository должен быть общим: common={common}"

    # JWT только в первом
    assert "JWT" in only1, f"JWT должен быть только в backend: only1={only1}"

    # Session только во втором
    assert "Session" in only2, f"Session должен быть только в frontend: only2={only2}"
