"""Раскладка по агентам.

Главный тест — про направление ссылки. Зеркальные сигнатуры у парных функций один раз
уже привели к тому, что ссылка создавалась внутри репозитория, а отчёт рапортовал успех:
сообщение «связан» было правдой про вызов и ложью про результат.
"""

from __future__ import annotations

import os
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from chekan import agents  # noqa: E402


@pytest.fixture
def source(tmp_path: Path) -> Path:
    src = tmp_path / "repo" / "skills" / "chekan"
    src.mkdir(parents=True)
    (src / "SKILL.md").write_text("---\nname: chekan\n---\n", encoding="utf-8")
    return src


@pytest.fixture
def agent_dir(tmp_path: Path) -> Path:
    d = tmp_path / "agent" / "skills"
    d.mkdir(parents=True)
    return d


def test_link_lands_in_the_agent_dir_not_in_the_repo(agent_dir: Path, source: Path):
    agents.link_skill(agent_dir, source, dry=False)
    link = agent_dir / "chekan"
    assert link.is_symlink(), "ссылки нет там, где её ждёт агент"
    # По иноду, а не по строке: под proot resolve() у ссылки и у источника
    # расходится, хотя это одна и та же папка.
    assert os.path.samefile(link, source)
    assert not (source / "chekan").exists(), "ссылка уехала внутрь репозитория"
    assert (link / "SKILL.md").is_file(), "скилл не читается через ссылку"


def test_dry_run_writes_nothing_and_says_so(agent_dir: Path, source: Path):
    verb = agents.link_skill(agent_dir, source, dry=True)
    assert not (agent_dir / "chekan").exists()
    assert "будет" in verb, f"сухой прогон отчитался как о свершившемся: {verb!r}"


def test_link_is_idempotent(agent_dir: Path, source: Path):
    agents.link_skill(agent_dir, source, dry=False)
    assert agents.link_skill(agent_dir, source, dry=False) == "уже связан"


def test_foreign_directory_is_left_alone(agent_dir: Path, source: Path):
    foreign = agent_dir / "chekan"
    foreign.mkdir()
    (foreign / "SKILL.md").write_text("чужой скилл\n", encoding="utf-8")
    verb = agents.link_skill(agent_dir, source, dry=False)
    assert "не трогаем" in verb
    assert (foreign / "SKILL.md").read_text(encoding="utf-8") == "чужой скилл\n"


def test_unlink_removes_only_our_link(agent_dir: Path, source: Path, tmp_path: Path):
    other = tmp_path / "other"
    other.mkdir()
    (agent_dir / "chekan").symlink_to(other)
    assert "не к нам" in agents.unlink_skill(agent_dir, source, dry=False)
    assert (agent_dir / "chekan").is_symlink()

    (agent_dir / "chekan").unlink()
    agents.link_skill(agent_dir, source, dry=False)
    assert agents.unlink_skill(agent_dir, source, dry=False) == "убрана"
    assert not (agent_dir / "chekan").exists()


# --- указатель в файле инструкций ---


def test_pointer_is_appended_without_touching_the_rest(tmp_path: Path):
    f = tmp_path / "AGENTS.md"
    f.write_text("# Мои правила\n\nНичего не удалять.\n", encoding="utf-8")
    agents.upsert_pointer(f, dry=False)
    text = f.read_text(encoding="utf-8")
    assert "Ничего не удалять." in text
    assert agents.START in text and agents.END in text
    assert list(f.parent.glob("AGENTS.md.bak-*")), "копия перед записью не сделана"


def test_pointer_updates_in_place_and_does_not_multiply(tmp_path: Path):
    f = tmp_path / "AGENTS.md"
    f.write_text("шапка\n", encoding="utf-8")
    agents.upsert_pointer(f, dry=False)
    agents.upsert_pointer(f, dry=False)
    assert f.read_text(encoding="utf-8").count(agents.START) == 1


def test_pointer_removal_leaves_the_rest(tmp_path: Path):
    f = tmp_path / "AGENTS.md"
    f.write_text("моё правило\n", encoding="utf-8")
    agents.upsert_pointer(f, dry=False)
    agents.remove_pointer(f, dry=False)
    text = f.read_text(encoding="utf-8")
    assert "моё правило" in text
    assert agents.START not in text


def test_pointer_stays_small(tmp_path: Path):
    """Указатель попадает в контекст КАЖДОЙ сессии агента — он обязан быть крошечным."""
    assert len(agents.POINTER) < 700, f"{len(agents.POINTER)} символов — слишком много"


def test_our_link_is_recognised_by_its_written_target(agent_dir: Path, source: Path):
    """Под proot одна папка даёт два разных пути после resolve().

    Сравнение только по resolve() делало раскладку неидемпотентной на Fold и
    не давало снять собственную ссылку: инструмент считал её чужой.
    """
    agents.link_skill(agent_dir, source, dry=False)
    link = agent_dir / "chekan"
    assert agents._points_at(link, source)

    # тот же путь, записанный иначе, — всё ещё наш
    assert agents.link_skill(agent_dir, Path(str(source) + "/."), dry=False) in (
        "уже связан", "связан",
    )
