import pytest
from typer.testing import CliRunner
from hayhooks.cli.base import hayhooks_cli
from unittest.mock import Mock


@pytest.fixture
def runner():
    return CliRunner()


def create_test_files(base_dir, file_dict):
    """Helper to create test files from a dictionary.

    Args:
        base_dir: The base directory to create files in
        file_dict: Dict mapping relative paths to file content

    Returns:
        Dictionary mapping filenames to Path objects
    """
    paths = {}
    for rel_path, content in file_dict.items():
        path = base_dir / rel_path
        path.parent.mkdir(parents=True, exist_ok=True)
        if content is not None:  # None means directory
            path.write_text(content)
        else:
            path.mkdir(exist_ok=True)
        paths[rel_path] = path
    return paths


def test_run_with_string_params(runner, monkeypatch):
    mock_run = Mock()
    monkeypatch.setattr("hayhooks.cli.pipeline.run_pipeline_with_files", mock_run)

    result = runner.invoke(
        hayhooks_cli, ["pipeline", "run", "test_pipeline", "--param", "key1=value1", "--param", "key2=value2"]
    )

    assert result.exit_code == 0
    mock_run.assert_called_once()
    _, kwargs = mock_run.call_args
    assert kwargs["pipeline_name"] == "test_pipeline"
    assert kwargs["params"] == {"key1": "value1", "key2": "value2"}
    assert kwargs["files"] == {}


def test_run_with_json_params(runner, monkeypatch):
    mock_run = Mock()
    monkeypatch.setattr("hayhooks.cli.pipeline.run_pipeline_with_files", mock_run)

    result = runner.invoke(
        hayhooks_cli,
        [
            "pipeline",
            "run",
            "test_pipeline",
            "--param",
            "list=[1,2,3]",
            "--param",
            "dict={\"a\":1,\"b\":2}",
            "--param",
            "list_of_strings=[\"a\",\"b\",\"c\"]",
        ],
    )

    assert result.exit_code == 0
    mock_run.assert_called_once()
    _, kwargs = mock_run.call_args
    assert kwargs["pipeline_name"] == "test_pipeline"
    assert kwargs["params"] == {"list": [1, 2, 3], "dict": {"a": 1, "b": 2}, "list_of_strings": ["a", "b", "c"]}


def test_run_with_invalid_param_format(runner):
    result = runner.invoke(hayhooks_cli, ["pipeline", "run", "test_pipeline", "--param", "invalid_format"])

    assert result.exit_code != 0
    assert "Invalid parameter format" in result.stdout


def test_run_with_file(runner, tmp_path, monkeypatch):
    files = create_test_files(tmp_path, {"test.txt": "test content"})

    mock_run = Mock()
    monkeypatch.setattr("hayhooks.cli.pipeline.run_pipeline_with_files", mock_run)

    result = runner.invoke(hayhooks_cli, ["pipeline", "run", "test_pipeline", "--file", str(files["test.txt"])])

    assert result.exit_code == 0
    mock_run.assert_called_once()
    _, kwargs = mock_run.call_args
    assert kwargs["pipeline_name"] == "test_pipeline"
    assert len(kwargs["files"]) == 1
    assert "test.txt" in kwargs["files"]


def test_run_with_mixed_params(runner, monkeypatch):
    mock_run = Mock()
    monkeypatch.setattr("hayhooks.cli.pipeline.run_pipeline_with_files", mock_run)

    result = runner.invoke(
        hayhooks_cli,
        [
            "pipeline",
            "run",
            "test_pipeline",
            "--param",
            "string=value",
            "--param",
            "number=42",
            "--param",
            "array=[1,2,3]",
        ],
    )

    assert result.exit_code == 0
    mock_run.assert_called_once()
    _, kwargs = mock_run.call_args
    assert kwargs["pipeline_name"] == "test_pipeline"
    assert kwargs["params"] == {
        "string": "value",
        "number": 42,  # Should be converted to integer
        "array": [1, 2, 3],  # Should be parsed as JSON array
    }


def test_run_with_complex_json_param(runner, monkeypatch):
    complex_json = '{"name":"test","values":[1,2,3],"nested":{"key":"value"}}'

    mock_run = Mock()
    monkeypatch.setattr("hayhooks.cli.pipeline.run_pipeline_with_files", mock_run)

    result = runner.invoke(hayhooks_cli, ["pipeline", "run", "test_pipeline", "--param", f"complex={complex_json}"])

    assert result.exit_code == 0
    mock_run.assert_called_once()
    _, kwargs = mock_run.call_args
    assert kwargs["pipeline_name"] == "test_pipeline"
    assert kwargs["params"]["complex"] == {"name": "test", "values": [1, 2, 3], "nested": {"key": "value"}}


def test_run_with_nonexistent_file(runner, tmp_path):
    nonexistent_file = "nonexistent.txt"

    result = runner.invoke(hayhooks_cli, ["pipeline", "run", "test_pipeline", "--file", str(nonexistent_file)])

    assert result.exit_code != 0
    assert "does not exist" in result.stdout


def test_run_with_directory(runner, tmp_path, monkeypatch):
    files = create_test_files(
        tmp_path,
        {
            "test_dir": None,  # None indicates directory
            "test_dir/file1.txt": "file1 content",
            "test_dir/file2.txt": "file2 content",
            "test_dir/.hidden": "hidden content",
        },
    )

    mock_run = Mock()
    monkeypatch.setattr("hayhooks.cli.pipeline.run_pipeline_with_files", mock_run)

    result = runner.invoke(hayhooks_cli, ["pipeline", "run", "test_pipeline", "--dir", str(files["test_dir"])])

    assert result.exit_code == 0
    mock_run.assert_called_once()
    _, kwargs = mock_run.call_args
    assert kwargs["pipeline_name"] == "test_pipeline"
    assert len(kwargs["files"]) == 2
    assert "file1.txt" in kwargs["files"]
    assert "file2.txt" in kwargs["files"]
    # Hidden file should be excluded
    assert not any(str(path).endswith(".hidden") for path in kwargs["files"].values())


def test_run_with_multiple_sources(runner, tmp_path, monkeypatch):
    files = create_test_files(
        tmp_path,
        {
            "test1.txt": "test1 content",
            "test2.txt": "test2 content",
            "test_dir": None,
            "test_dir/dir_file.txt": "dir file content",
        },
    )

    mock_run = Mock()
    monkeypatch.setattr("hayhooks.cli.pipeline.run_pipeline_with_files", mock_run)

    result = runner.invoke(
        hayhooks_cli,
        [
            "pipeline",
            "run",
            "test_pipeline",
            "--file",
            str(files["test1.txt"]),
            "--file",
            str(files["test2.txt"]),
            "--dir",
            str(files["test_dir"]),
            "--param",
            "key1=value1",
            "--param",
            "key2=[1,2,3]",
        ],
    )

    assert result.exit_code == 0

    mock_run.assert_called_once()
    _, kwargs = mock_run.call_args
    assert kwargs["pipeline_name"] == "test_pipeline"
    # Check files (3 total: 2 direct files + 1 from directory)
    assert len(kwargs["files"]) == 3
    # Check params
    assert kwargs["params"] == {"key1": "value1", "key2": [1, 2, 3]}
