import io
from pathlib import Path

from cotext_delta.approval import ApprovalOptions
from cotext_delta.chat_session import ChatSession
from cotext_delta.llm_client import LLMResult, ToolCall
from cotext_delta.logger import Logger
from cotext_delta.tool_registry import ToolRegistry


class FakeLLM:
    def __init__(self, results):
        self.results = results
        self.calls = 0

    def invoke(self, messages, tools=None):
        result = self.results[self.calls]
        self.calls += 1
        return result


def make_logger():
    return Logger(stream_info=io.StringIO(), stream_error=io.StringIO())


def test_scenario_keyword_replace(tmp_path, monkeypatch):
    monkeypatch.chdir(tmp_path)
    target = tmp_path / "example.txt"
    target.write_text("foo\n", encoding="utf-8")
    fake = FakeLLM(
        [
            LLMResult(
                content="",
                tool_calls=[
                    ToolCall(id="1", name="grep_files", arguments={"pattern": "foo", "workdir": str(tmp_path)})
                ],
            ),
            LLMResult(
                content="",
                tool_calls=[
                    ToolCall(
                        id="2",
                        name="apply_patch",
                        arguments={
                            "patch_text": "*** Begin Patch\n*** Update File: example.txt\n@@\n-foo\n+bar\n*** End Patch"
                        },
                    )
                ],
            ),
            LLMResult(content="done", tool_calls=[]),
        ]
    )
    session = ChatSession(
        fake,
        registry=ToolRegistry(logger=make_logger()),
        logger=make_logger(),
        approval=ApprovalOptions(assume_yes=True),
    )
    out = session.run_turn("run scenario1")
    assert "done" in out or "bar" in target.read_text(encoding="utf-8")
    assert "bar" in target.read_text(encoding="utf-8")


def test_scenario_web_search_to_file(tmp_path, monkeypatch):
    monkeypatch.setenv("SEARCH_PROVIDER", "mock")
    monkeypatch.chdir(tmp_path)
    fake = FakeLLM(
        [
            LLMResult(
                content="",
                tool_calls=[ToolCall(id="1", name="web_search", arguments={"query": "weather"})],
            ),
            LLMResult(
                content="",
                tool_calls=[
                    ToolCall(
                        id="2",
                        name="apply_patch",
                        arguments={
                            "patch_text": "*** Begin Patch\n*** Add File: weather.txt\n+mock web_search results\n*** End Patch"
                        },
                    )
                ],
            ),
            LLMResult(content="done", tool_calls=[]),
        ]
    )
    session = ChatSession(
        fake,
        registry=ToolRegistry(logger=make_logger()),
        logger=make_logger(),
        approval=ApprovalOptions(assume_yes=True),
    )
    out = session.run_turn("run scenario2")
    assert "done" in out or (tmp_path / "weather.txt").exists()
    assert "mock web_search results" in (tmp_path / "weather.txt").read_text(encoding="utf-8")


def test_scenario_list_dir_report(tmp_path, monkeypatch):
    monkeypatch.chdir(tmp_path)
    (tmp_path / "src").mkdir()
    (tmp_path / "src" / "module").mkdir()
    fake = FakeLLM(
        [
            LLMResult(
                content="",
                tool_calls=[ToolCall(id="1", name="list_dir", arguments={"dir": str(tmp_path), "depth": 2})],
            ),
            LLMResult(
                content="",
                tool_calls=[
                    ToolCall(
                        id="2",
                        name="apply_patch",
                        arguments={
                            "patch_text": "*** Begin Patch\n*** Add File: report.txt\n+report\n*** End Patch"
                        },
                    )
                ],
            ),
            LLMResult(content="done", tool_calls=[]),
        ]
    )
    session = ChatSession(
        fake,
        registry=ToolRegistry(logger=make_logger()),
        logger=make_logger(),
        approval=ApprovalOptions(assume_yes=True),
    )
    out = session.run_turn("run scenario3")
    assert "done" in out or (tmp_path / "report.txt").exists()
    assert "report" in (tmp_path / "report.txt").read_text(encoding="utf-8")


def test_scenario_exec_and_log(tmp_path, monkeypatch):
    monkeypatch.chdir(tmp_path)
    fake = FakeLLM(
        [
            LLMResult(
                content="",
                tool_calls=[
                    ToolCall(
                        id="1",
                        name="exec_command",
                        arguments={"command": "echo phase3", "session": "s1", "workdir": str(tmp_path)},
                    )
                ],
            ),
            LLMResult(
                content="",
                tool_calls=[
                    ToolCall(
                        id="2",
                        name="apply_patch",
                        arguments={
                            "patch_text": "*** Begin Patch\n*** Add File: exec_log.txt\n+phase3\n*** End Patch"
                        },
                    )
                ],
            ),
            LLMResult(content="done", tool_calls=[]),
        ]
    )
    session = ChatSession(
        fake,
        registry=ToolRegistry(logger=make_logger()),
        logger=make_logger(),
        approval=ApprovalOptions(assume_yes=True),
    )
    out = session.run_turn("run scenario4")
    assert "done" in out or (tmp_path / "exec_log.txt").exists()
    assert "phase3" in (tmp_path / "exec_log.txt").read_text(encoding="utf-8")
