"""Tests for grep and find skills."""

import os

import pytest

from fliiq.runtime.package_data import bundled_skills_dir
from fliiq.runtime.skills.base import SkillBase

SKILLS_DIR = str(bundled_skills_dir())


def _load_skill(name: str) -> SkillBase:
    return SkillBase(os.path.join(SKILLS_DIR, name))


# --- grep ---


async def test_grep_basic(tmp_path):
    skill = _load_skill("grep")
    f = tmp_path / "test.py"
    f.write_text("def hello():\n    print('world')\n\ndef goodbye():\n    pass\n")

    result = await skill.execute({"pattern": "def \\w+", "path": str(tmp_path)})
    assert "hello" in result["matches"]
    assert "goodbye" in result["matches"]


async def test_grep_case_insensitive(tmp_path):
    skill = _load_skill("grep")
    f = tmp_path / "test.txt"
    f.write_text("Hello World\nhello world\nHELLO WORLD\n")

    result = await skill.execute({"pattern": "hello", "path": str(tmp_path), "ignore_case": True})
    assert result["matches"].count(":") >= 3


async def test_grep_with_context(tmp_path):
    skill = _load_skill("grep")
    f = tmp_path / "context.txt"
    f.write_text("line1\nline2\nMATCH\nline4\nline5\n")

    result = await skill.execute({"pattern": "MATCH", "path": str(tmp_path), "context": 1})
    assert "line2" in result["matches"]
    assert "line4" in result["matches"]


async def test_grep_no_matches(tmp_path):
    skill = _load_skill("grep")
    f = tmp_path / "empty.txt"
    f.write_text("nothing here\n")

    result = await skill.execute({"pattern": "xyz_missing", "path": str(tmp_path)})
    assert "No matches" in result["matches"]


async def test_grep_invalid_regex(tmp_path):
    skill = _load_skill("grep")
    with pytest.raises(ValueError, match="Invalid regex"):
        await skill.execute({"pattern": "[invalid", "path": str(tmp_path)})


async def test_grep_limit(tmp_path):
    skill = _load_skill("grep")
    f = tmp_path / "many.txt"
    f.write_text("\n".join(f"match line {i}" for i in range(50)))

    result = await skill.execute({"pattern": "match", "path": str(tmp_path), "limit": 5})
    # Should have at most 5 match lines
    lines = [l for l in result["matches"].split("\n") if l.strip()]
    assert len(lines) <= 5


async def test_grep_single_file(tmp_path):
    skill = _load_skill("grep")
    f = tmp_path / "single.txt"
    f.write_text("target line\nother line\n")

    result = await skill.execute({"pattern": "target", "path": str(f)})
    assert "target line" in result["matches"]


# --- find ---


async def test_find_basic(tmp_path):
    skill = _load_skill("find")
    (tmp_path / "a.py").write_text("")
    (tmp_path / "b.py").write_text("")
    (tmp_path / "c.txt").write_text("")

    result = await skill.execute({"pattern": "*.py", "path": str(tmp_path)})
    assert "a.py" in result["files"]
    assert "b.py" in result["files"]
    assert "c.txt" not in result["files"]


async def test_find_recursive(tmp_path):
    skill = _load_skill("find")
    sub = tmp_path / "sub"
    sub.mkdir()
    (sub / "deep.py").write_text("")
    (tmp_path / "top.py").write_text("")

    result = await skill.execute({"pattern": "*.py", "path": str(tmp_path)})
    assert "deep.py" in result["files"]
    assert "top.py" in result["files"]


async def test_find_no_results(tmp_path):
    skill = _load_skill("find")
    result = await skill.execute({"pattern": "*.xyz", "path": str(tmp_path)})
    assert "No files found" in result["files"]


async def test_find_skips_git(tmp_path):
    skill = _load_skill("find")
    git_dir = tmp_path / ".git"
    git_dir.mkdir()
    (git_dir / "config.py").write_text("")
    (tmp_path / "real.py").write_text("")

    result = await skill.execute({"pattern": "*.py", "path": str(tmp_path)})
    assert "real.py" in result["files"]
    assert "config.py" not in result["files"]


async def test_find_limit(tmp_path):
    skill = _load_skill("find")
    for i in range(20):
        (tmp_path / f"file{i}.txt").write_text("")

    result = await skill.execute({"pattern": "*.txt", "path": str(tmp_path), "limit": 5})
    lines = [l for l in result["files"].split("\n") if l.strip()]
    assert len(lines) == 5
