from __future__ import annotations

import pytest

from revibe.core.config import SessionLoggingConfig, VibeConfig
from revibe.core.middleware import (
    PLAN_MODE_REMINDER,
    ConversationContext,
    MiddlewareAction,
    MiddlewarePipeline,
    PlanModeMiddleware,
)
from revibe.core.modes import AgentMode
from revibe.core.types import AgentStats


def make_context() -> ConversationContext:
    config = VibeConfig(session_logging=SessionLoggingConfig(enabled=False))
    return ConversationContext(messages=[], stats=AgentStats(), config=config)


class TestPlanModeMiddleware:
    @pytest.mark.asyncio
    async def test_injects_reminder_when_plan_mode_active(self) -> None:
        middleware = PlanModeMiddleware(lambda: AgentMode.PLAN)
        ctx = make_context()

        result = await middleware.before_turn(ctx)

        assert result.action == MiddlewareAction.INJECT_MESSAGE
        assert result.message == PLAN_MODE_REMINDER

    @pytest.mark.asyncio
    async def test_does_not_inject_when_default_mode(self) -> None:
        middleware = PlanModeMiddleware(lambda: AgentMode.DEFAULT)
        ctx = make_context()

        result = await middleware.before_turn(ctx)

        assert result.action == MiddlewareAction.CONTINUE
        assert result.message is None

    @pytest.mark.asyncio
    async def test_does_not_inject_when_auto_approve_mode(self) -> None:
        middleware = PlanModeMiddleware(lambda: AgentMode.AUTO_APPROVE)
        ctx = make_context()

        result = await middleware.before_turn(ctx)

        assert result.action == MiddlewareAction.CONTINUE
        assert result.message is None

    @pytest.mark.asyncio
    async def test_does_not_inject_when_accept_edits_mode(self) -> None:
        middleware = PlanModeMiddleware(lambda: AgentMode.ACCEPT_EDITS)
        ctx = make_context()

        result = await middleware.before_turn(ctx)

        assert result.action == MiddlewareAction.CONTINUE
        assert result.message is None

    @pytest.mark.asyncio
    async def test_after_turn_always_continues(self) -> None:
        middleware = PlanModeMiddleware(lambda: AgentMode.PLAN)
        ctx = make_context()

        result = await middleware.after_turn(ctx)

        assert result.action == MiddlewareAction.CONTINUE

    @pytest.mark.asyncio
    async def test_dynamically_checks_mode(self) -> None:
        current_mode = AgentMode.DEFAULT
        middleware = PlanModeMiddleware(lambda: current_mode)
        ctx = make_context()

        result = await middleware.before_turn(ctx)
        assert result.action == MiddlewareAction.CONTINUE

        current_mode = AgentMode.PLAN
        result = await middleware.before_turn(ctx)
        assert result.action == MiddlewareAction.INJECT_MESSAGE

        current_mode = AgentMode.AUTO_APPROVE
        result = await middleware.before_turn(ctx)
        assert result.action == MiddlewareAction.CONTINUE

    @pytest.mark.asyncio
    async def test_custom_reminder(self) -> None:
        custom_reminder = "Custom plan mode reminder"
        middleware = PlanModeMiddleware(
            lambda: AgentMode.PLAN, reminder=custom_reminder
        )
        ctx = make_context()

        result = await middleware.before_turn(ctx)

        assert result.message == custom_reminder

    def test_reset_does_nothing(self) -> None:
        middleware = PlanModeMiddleware(lambda: AgentMode.PLAN)
        middleware.reset()


class TestMiddlewarePipelineWithPlanMode:
    @pytest.mark.asyncio
    async def test_pipeline_includes_plan_mode_injection(self) -> None:
        pipeline = MiddlewarePipeline()
        pipeline.add(PlanModeMiddleware(lambda: AgentMode.PLAN))
        ctx = make_context()

        result = await pipeline.run_before_turn(ctx)

        assert result.action == MiddlewareAction.INJECT_MESSAGE
        assert PLAN_MODE_REMINDER in (result.message or "")

    @pytest.mark.asyncio
    async def test_pipeline_skips_injection_when_not_plan_mode(self) -> None:
        pipeline = MiddlewarePipeline()
        pipeline.add(PlanModeMiddleware(lambda: AgentMode.DEFAULT))
        ctx = make_context()

        result = await pipeline.run_before_turn(ctx)

        assert result.action == MiddlewareAction.CONTINUE
