"""Tests for @ file reference resolver."""

from __future__ import annotations

from pathlib import Path

import pytest

from clients.terminal.tui.file_refs import FileRefResolver, MAX_FILE_BYTES, MAX_TOTAL_BYTES


@pytest.fixture
def sample_dir(tmp_path: Path) -> Path:
    d = tmp_path / "project"
    d.mkdir()
    (d / "main.py").write_text("def main():\n    pass\n", encoding="utf-8")
    (d / "readme.md").write_text("# Hello\n", encoding="utf-8")
    (d / "sub").mkdir()
    (d / "sub" / "util.py").write_text("def util():\n    return 1\n", encoding="utf-8")
    return d


def test_resolve_single_file(sample_dir: Path) -> None:
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("check @main.py")
    assert not result.errors
    assert len(result.attachments) == 1
    assert result.attachments[0].display_path == "main.py"
    assert "def main():" in result.to_message()
    assert "```python file: main.py" in result.to_message()


def test_resolve_directory_without_recursive(sample_dir: Path) -> None:
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("look @.")
    assert not result.errors
    paths = {a.display_path for a in result.attachments}
    assert "main.py" in paths
    assert "readme.md" in paths
    assert "sub/util.py" not in paths


def test_resolve_directory_recursive(sample_dir: Path) -> None:
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("look @./")
    paths = {a.display_path for a in result.attachments}
    assert "main.py" in paths
    assert "sub/util.py" in paths


def test_resolve_missing_file(sample_dir: Path) -> None:
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("check @missing.py")
    assert not result.attachments
    assert any("not found" in e for e in result.errors)


def test_resolve_glob(sample_dir: Path) -> None:
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("check @**/*.py")
    paths = {a.display_path for a in result.attachments}
    assert "main.py" in paths
    assert "sub/util.py" in paths
    assert "readme.md" not in paths


def test_resolve_tilde_expands(tmp_path: Path, monkeypatch) -> None:
    home = tmp_path / "home"
    home.mkdir()
    (home / "note.txt").write_text("hello", encoding="utf-8")
    monkeypatch.setenv("HOME", str(home))
    resolver = FileRefResolver(base_dir=tmp_path)
    result = resolver.resolve("read @~/note.txt")
    assert len(result.attachments) == 1
    assert result.attachments[0].content == "hello"


def test_no_refs_returns_unchanged() -> None:
    resolver = FileRefResolver(Path.cwd())
    text = "just a regular message"
    result = resolver.resolve(text)
    assert result.prompt == text
    assert result.is_empty()


def test_size_limit_per_file(sample_dir: Path) -> None:
    big = sample_dir / "big.txt"
    big.write_bytes(b"x" * (MAX_FILE_BYTES + 100))
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("read @big.txt")
    assert len(result.attachments) == 1
    assert result.attachments[0].truncated
    assert len(result.attachments[0].content) <= MAX_FILE_BYTES + 100


def test_total_size_limit_stops_processing(sample_dir: Path) -> None:
    # Each file is exactly half the total limit; a third file should be skipped.
    big1 = sample_dir / "big1.txt"
    big2 = sample_dir / "big2.txt"
    big3 = sample_dir / "big3.txt"
    big1.write_bytes(b"x" * MAX_FILE_BYTES)
    big2.write_bytes(b"x" * MAX_FILE_BYTES)
    big3.write_bytes(b"x" * 100)
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("read @big1.txt @big2.txt @big3.txt")
    assert result.total_bytes <= MAX_TOTAL_BYTES
    assert len(result.attachments) == 2
    assert any("limit reached" in e for e in result.errors)


def test_resolve_absolute_path_outside_base_rejected(sample_dir: Path) -> None:
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("check @/etc/passwd")
    assert not result.attachments
    assert any("not found" in e or "could not resolve" in e for e in result.errors)


def test_resolve_sensitive_file_skipped(sample_dir: Path) -> None:
    (sample_dir / ".env").write_text("SECRET=123\n", encoding="utf-8")
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("read @.env")
    assert not result.attachments
    assert any("skipped sensitive file" in e for e in result.errors)


def test_resolve_binary_file_skipped(sample_dir: Path) -> None:
    (sample_dir / "binary.bin").write_bytes(b"\x00\x01\x02\x03")
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("read @binary.bin")
    assert not result.attachments
    assert any("skipped binary file" in e for e in result.errors)


def test_resolve_glob_with_brackets(sample_dir: Path) -> None:
    (sample_dir / "a.py").write_text("a", encoding="utf-8")
    (sample_dir / "b.py").write_text("b", encoding="utf-8")
    resolver = FileRefResolver(sample_dir)
    result = resolver.resolve("check @[ab].py")
    paths = {a.display_path for a in result.attachments}
    assert "a.py" in paths
    assert "b.py" in paths
