import logging
import os
from pathlib import Path
from string import ascii_lowercase

import pytest

from mcpunk.util import create_file_tree, log_inputs_outputs, matches_filter, rand_str


@pytest.mark.parametrize("log_level", (logging.DEBUG, logging.INFO, "WARNING"))
def test_log_inputs(
    caplog: pytest.LogCaptureFixture,
    log_level: int | str,
) -> None:
    """Test log_inputs decorator captures function inputs and outputs."""
    caplog.set_level(logging.DEBUG)

    @log_inputs_outputs(log_level=log_level)
    def example_func(a: int, *, b: str = "test") -> str:
        return f"{a}{b}"

    result = example_func(1, b="value")
    assert result == "1value"

    expect_log_to_contain_snippets = [
        "Calling tool example_func with inputs:",
        "Arg_0=1",
        "b='value'",
        "resp='1value'",
    ]
    for t in expect_log_to_contain_snippets:
        assert t in caplog.text, t
        for record in caplog.records:
            if t in record.message:
                assert log_level in (record.levelno, record.levelname), record.levelno

    assert "Calling tool example_func with inputs:" in caplog.text
    assert "Arg_0=1" in caplog.text
    assert "b='value'" in caplog.text
    assert "resp='1value'" in caplog.text


def test_create_file_tree(tmp_path: Path) -> None:
    """Test create_file_tree generates correct compact text output."""
    # Set up test files
    (tmp_path / "dir1").mkdir()
    (tmp_path / "dir1/file1.txt").touch()
    (tmp_path / "dir1/other.txt").touch()
    (tmp_path / "dir1/subdir").mkdir()
    (tmp_path / "dir1/subdir/file2.txt").touch()
    (tmp_path / "file3.txt").touch()
    (tmp_path / "dir2/dir4/dir5").mkdir(parents=True)
    (tmp_path / "logs").mkdir()
    (tmp_path / "logs/app.log").touch()
    (tmp_path / "logs/error.log").touch()

    paths = {
        tmp_path / "dir1",
        tmp_path / "dir1/file1.txt",
        tmp_path / "dir1/other.txt",
        tmp_path / "dir1/subdir",
        tmp_path / "dir1/subdir/file2.txt",
        tmp_path / "file3.txt",
        tmp_path / "dir2/dir4/dir5",
        tmp_path / "logs",
        tmp_path / "logs/app.log",
        tmp_path / "logs/error.log",
    }

    result = create_file_tree(
        project_root=tmp_path,
        paths=paths,
    )

    sep = os.path.sep
    expected = (
        f".: file3.txt\ndir1: file1.txt; other.txt\ndir1{sep}subdir: file2.txt\n"
        "logs: app.log; error.log\n"
    )
    assert result == expected


def test_create_file_tree_with_filter(tmp_path: Path) -> None:
    """Test create_file_tree with filter parameter."""
    (tmp_path / "test.py").touch()
    (tmp_path / "main.py").touch()
    (tmp_path / "data.txt").touch()

    paths = {
        tmp_path / "test.py",
        tmp_path / "main.py",
        tmp_path / "data.txt",
    }

    result = create_file_tree(
        project_root=tmp_path,
        paths=paths,
        filter_=[".py"],
    )
    assert result == ".: main.py; test.py\n"


def test_create_file_tree_depth_limit(tmp_path: Path) -> None:
    """Test create_file_tree with depth limit."""
    (tmp_path / "file0.txt").touch()
    (tmp_path / "dir1/dir2/dir3").mkdir(parents=True)
    (tmp_path / "dir1/file1.txt").touch()
    (tmp_path / "dir1/dir2/file2.txt").touch()
    (tmp_path / "dir1/dir2/dir3/file3.txt").touch()

    paths = {
        tmp_path / p
        for p in [
            "file0.txt",
            "dir1",
            "dir1/file1.txt",
            "dir1/dir2",
            "dir1/dir2/file2.txt",
            "dir1/dir2/dir3",
            "dir1/dir2/dir3/file3.txt",
        ]
    }

    result = create_file_tree(
        project_root=tmp_path,
        paths=paths,
        limit_depth_from_root=2,
    )
    assert result == ".: file0.txt\ndir1: file1.txt\n"


def test_rand_str() -> None:
    """Test rand_str generates strings correctly."""
    # Test default length
    result = rand_str()
    assert len(result) == 10
    assert all(c in ascii_lowercase for c in result)

    # Test custom length
    result = rand_str(n=5)
    assert len(result) == 5

    # Test custom characters
    result = rand_str(chars="ABC")
    assert len(result) == 10
    assert all(c in "ABC" for c in result)


def test_matches_filter() -> None:
    """Test matches_filter with different filter types."""
    # Test None filter
    assert matches_filter(None, "test") is True
    assert matches_filter(None, None) is True

    # Test list filter
    assert matches_filter(["abc", "def"], "abcdef") is True
    assert matches_filter(["xyz", "123"], "abcdef") is False
    assert matches_filter(["abc"], None) is False
