from __future__ import annotations

import pytest

from tests.mock.mock_backend_factory import mock_backend_factory
from tests.mock.utils import mock_llm_chunk
from tests.stubs.fake_backend import FakeBackend
from vibe.core import run_programmatic
from vibe.core.config import Backend, SessionLoggingConfig, VibeConfig
from vibe.core.types import LLMMessage, OutputFormat, Role


class SpyStreamingFormatter:
    def __init__(self) -> None:
        self.emitted: list[tuple[Role, str | None]] = []

    def on_message_added(self, message: LLMMessage) -> None:
        self.emitted.append((message.role, message.content))

    def on_event(self, _event) -> None:  # No-op for this test
        pass

    def finalize(self) -> str | None:
        return None


def test_run_programmatic_preload_streaming_is_batched(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    spy = SpyStreamingFormatter()
    monkeypatch.setattr(
        "vibe.core.programmatic.create_formatter", lambda *_args, **_kwargs: spy
    )

    with mock_backend_factory(
        Backend.MISTRAL,
        lambda provider, **kwargs: FakeBackend([
            mock_llm_chunk(
                content="Decorators are wrappers that modify function behavior.",
                finish_reason="stop",
            )
        ]),
    ):
        cfg = VibeConfig(
            session_logging=SessionLoggingConfig(enabled=False),
            system_prompt_id="tests",
            include_project_context=False,
            include_prompt_detail=False,
            include_model_info=False,
            include_commit_signature=False,
        )

        previous = [
            LLMMessage(
                role=Role.system, content="This system message should be ignored."
            ),
            LLMMessage(
                role=Role.user, content="Previously, you told me about decorators."
            ),
            LLMMessage(
                role=Role.assistant,
                content="Sure, decorators allow you to wrap functions.",
            ),
        ]

        run_programmatic(
            config=cfg,
            prompt="Can you summarize what decorators are?",
            output_format=OutputFormat.STREAMING,
            previous_messages=previous,
        )

        roles = [r for r, _ in spy.emitted]
        assert roles == [
            Role.system,
            Role.user,
            Role.assistant,
            Role.user,
            Role.assistant,
        ]
        assert (
            spy.emitted[0][1] == "You are Vibe, a super useful programming assistant."
        )
        assert spy.emitted[1][1] == "Previously, you told me about decorators."
        assert spy.emitted[2][1] == "Sure, decorators allow you to wrap functions."
        assert spy.emitted[3][1] == "Can you summarize what decorators are?"
        assert (
            spy.emitted[4][1]
            == "Decorators are wrappers that modify function behavior."
        )


def test_run_programmatic_ignores_system_messages_in_previous(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    spy = SpyStreamingFormatter()
    monkeypatch.setattr(
        "vibe.core.programmatic.create_formatter", lambda *_args, **_kwargs: spy
    )

    with mock_backend_factory(
        Backend.MISTRAL,
        lambda provider, **kwargs: FakeBackend([mock_llm_chunk(content="Understood.")]),
    ):
        cfg = VibeConfig(
            session_logging=SessionLoggingConfig(enabled=False),
            system_prompt_id="tests",
            include_project_context=False,
            include_prompt_detail=False,
            include_model_info=False,
            include_commit_signature=False,
        )

        run_programmatic(
            config=cfg,
            prompt="Let's move on to practical examples.",
            output_format=OutputFormat.STREAMING,
            previous_messages=[
                LLMMessage(
                    role=Role.system,
                    content="First system message that should be ignored.",
                ),
                LLMMessage(role=Role.user, content="Continue our previous discussion."),
                LLMMessage(
                    role=Role.system,
                    content="Second system message that should be ignored.",
                ),
            ],
            auto_approve=True,
        )

        roles = [r for r, _ in spy.emitted]
        assert roles == [Role.system, Role.user, Role.user, Role.assistant]
        assert (
            spy.emitted[0][1] == "You are Vibe, a super useful programming assistant."
        )
        assert spy.emitted[1][1] == "Continue our previous discussion."
        assert spy.emitted[2][1] == "Let's move on to practical examples."
        assert spy.emitted[3][1] == "Understood."
