"""
Tests for kodara.repos.manager — registry of indexed projects.

Run: pytest tests/test_repos.py -v
"""

from __future__ import annotations

import json
import time
from pathlib import Path

import pytest

from kodara.repos.manager import (
    get_registry_path,
    register_repo,
    list_repos,
    clean_repos,
    _read_registry,
    _write_registry,
)


# ── Fixtures ──────────────────────────────────────────────────────────────────


@pytest.fixture(autouse=True)
def isolated_registry(tmp_path, monkeypatch):
    """Redirect ~/.kodara/ to a temp dir so tests don't touch the real registry."""
    fake_home = tmp_path / "home"
    fake_home.mkdir()
    monkeypatch.setenv("HOME", str(fake_home))
    # Also patch Path.home() via monkeypatch
    monkeypatch.setattr(Path, "home", staticmethod(lambda: fake_home))
    yield fake_home


# ── get_registry_path ─────────────────────────────────────────────────────────


def test_get_registry_path_returns_path_in_home():
    reg = get_registry_path()
    assert isinstance(reg, Path)
    assert reg.name == "repos.json"
    assert ".kodara" in str(reg)


def test_get_registry_path_creates_directory():
    reg = get_registry_path()
    assert reg.parent.exists()


# ── register_repo ─────────────────────────────────────────────────────────────


def test_register_repo_adds_new_path(tmp_path):
    project = tmp_path / "my-project"
    project.mkdir()

    register_repo(str(project))

    repos = _read_registry()
    paths = [r["path"] for r in repos]
    assert str(project.resolve()) in paths


def test_register_repo_no_duplicates(tmp_path):
    project = tmp_path / "my-project"
    project.mkdir()

    register_repo(str(project))
    register_repo(str(project))

    repos = _read_registry()
    matching = [r for r in repos if r["path"] == str(project.resolve())]
    assert len(matching) == 1


def test_register_repo_updates_last_seen(tmp_path):
    project = tmp_path / "my-project"
    project.mkdir()

    register_repo(str(project))
    t1 = _read_registry()[0]["last_seen"]

    time.sleep(0.05)
    register_repo(str(project))
    t2 = _read_registry()[0]["last_seen"]

    assert t2 >= t1


# ── list_repos ────────────────────────────────────────────────────────────────


def test_list_repos_existing_path_exists_true(tmp_path):
    project = tmp_path / "real-project"
    project.mkdir()
    # Create a minimal .kodara/index.db so exists=True
    kodara_dir = project / ".kodara"
    kodara_dir.mkdir()
    (kodara_dir / "index.db").write_bytes(b"")

    register_repo(str(project))

    entries = list_repos()
    matching = [e for e in entries if e.path == str(project.resolve())]
    assert len(matching) == 1
    assert matching[0].exists is True


def test_list_repos_nonexistent_path_exists_false(tmp_path):
    ghost = tmp_path / "ghost-project"
    # Register without creating the directory
    _write_registry([{"path": str(ghost.resolve()), "last_seen": time.time()}])

    entries = list_repos()
    matching = [e for e in entries if e.path == str(ghost.resolve())]
    assert len(matching) == 1
    assert matching[0].exists is False


def test_list_repos_empty_registry():
    entries = list_repos()
    assert entries == []


def test_list_repos_sorted_by_indexed_at_desc(tmp_path):
    # Two projects registered at different times
    p1 = tmp_path / "older"
    p2 = tmp_path / "newer"
    for p in (p1, p2):
        p.mkdir()
        (p / ".kodara").mkdir()
        (p / ".kodara" / "index.db").write_bytes(b"")

    _write_registry([
        {"path": str(p1.resolve()), "last_seen": 1000.0},
        {"path": str(p2.resolve()), "last_seen": 2000.0},
    ])

    entries = list_repos()
    # Without a real index.db the indexed_at falls back to last_seen
    # Just check both are returned and newer (last_seen=2000) is first
    assert len(entries) == 2
    assert entries[0].indexed_at >= entries[1].indexed_at


# ── clean_repos ───────────────────────────────────────────────────────────────


def test_clean_repos_removes_nonexistent(tmp_path):
    ghost = tmp_path / "ghost"
    _write_registry([{"path": str(ghost.resolve()), "last_seen": time.time()}])

    removed = clean_repos()

    assert str(ghost.resolve()) in removed
    remaining = _read_registry()
    assert all(r["path"] != str(ghost.resolve()) for r in remaining)


def test_clean_repos_keeps_existing(tmp_path):
    real = tmp_path / "real"
    real.mkdir()
    (real / ".kodara").mkdir()
    (real / ".kodara" / "index.db").write_bytes(b"")

    _write_registry([{"path": str(real.resolve()), "last_seen": time.time()}])

    removed = clean_repos()

    assert str(real.resolve()) not in removed
    remaining = _read_registry()
    assert any(r["path"] == str(real.resolve()) for r in remaining)


def test_clean_repos_returns_list_of_removed(tmp_path):
    ghost1 = tmp_path / "ghost1"
    ghost2 = tmp_path / "ghost2"
    real = tmp_path / "real"
    real.mkdir()
    (real / ".kodara").mkdir()
    (real / ".kodara" / "index.db").write_bytes(b"")

    _write_registry([
        {"path": str(ghost1.resolve()), "last_seen": time.time()},
        {"path": str(ghost2.resolve()), "last_seen": time.time()},
        {"path": str(real.resolve()), "last_seen": time.time()},
    ])

    removed = clean_repos()

    assert len(removed) == 2
    assert str(ghost1.resolve()) in removed
    assert str(ghost2.resolve()) in removed


def test_clean_repos_empty_registry():
    removed = clean_repos()
    assert removed == []


# ── Regression: existing imports still work ───────────────────────────────────


def test_existing_imports_work():
    from kodara.memory.store import MemoryStore, KodaraIndex
    from kodara.indexer.scanner import RepositoryScanner
    from kodara.graph.dependency import DependencyGraph
    from kodara.engine_client import query_context, query_impact

    assert MemoryStore is not None
    assert KodaraIndex is not None
    assert RepositoryScanner is not None
    assert DependencyGraph is not None
    assert query_context is not None
    assert query_impact is not None
