"""Tests for serverless detection, auto-flush on session exit, and exit warning."""

from __future__ import annotations

import os
from unittest.mock import MagicMock, patch

import pytest

from amplitude_ai.serverless import _reset_serverless_cache, is_serverless
from amplitude_ai.testing import MockAmplitudeAI


# -------------------------------------------------------------------
# is_serverless()
# -------------------------------------------------------------------


class TestIsServerless:
    def setup_method(self) -> None:
        _reset_serverless_cache()

    def teardown_method(self) -> None:
        _reset_serverless_cache()
        for var in (
            "VERCEL", "AWS_LAMBDA_FUNCTION_NAME", "NETLIFY",
            "FUNCTION_TARGET", "WEBSITE_INSTANCE_ID", "CF_PAGES",
        ):
            os.environ.pop(var, None)

    def test_returns_false_when_no_env_vars_set(self) -> None:
        assert is_serverless() is False

    def test_returns_true_when_vercel_set(self) -> None:
        os.environ["VERCEL"] = "1"
        assert is_serverless() is True

    def test_returns_true_when_lambda_set(self) -> None:
        os.environ["AWS_LAMBDA_FUNCTION_NAME"] = "my-function"
        assert is_serverless() is True

    def test_caches_result(self) -> None:
        assert is_serverless() is False
        os.environ["VERCEL"] = "1"
        assert is_serverless() is False  # cached
        _reset_serverless_cache()
        assert is_serverless() is True  # re-evaluated


# -------------------------------------------------------------------
# Session auto_flush
# -------------------------------------------------------------------


class TestSessionAutoFlush:
    def setup_method(self) -> None:
        _reset_serverless_cache()

    def teardown_method(self) -> None:
        _reset_serverless_cache()
        os.environ.pop("VERCEL", None)

    def test_auto_flush_defaults_false_non_serverless(self) -> None:
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")
        session = agent.session(session_id="s1")
        assert session.auto_flush is False

    def test_auto_flush_defaults_true_in_serverless(self) -> None:
        os.environ["VERCEL"] = "1"
        _reset_serverless_cache()
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")
        session = agent.session(session_id="s1")
        assert session.auto_flush is True

    def test_auto_flush_explicit_true(self) -> None:
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")
        session = agent.session(session_id="s1", auto_flush=True)
        assert session.auto_flush is True

    def test_auto_flush_explicit_false_in_serverless(self) -> None:
        os.environ["VERCEL"] = "1"
        _reset_serverless_cache()
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")
        session = agent.session(session_id="s1", auto_flush=False)
        assert session.auto_flush is False

    def test_sync_session_flushes_when_auto_flush_true(self) -> None:
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")

        with patch.object(mock, "flush", wraps=mock.flush) as flush_spy:
            with agent.session(session_id="s1", auto_flush=True) as s:
                s.track_user_message(content="hello")
            flush_spy.assert_called()

    def test_sync_session_does_not_flush_when_auto_flush_false(self) -> None:
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")

        with patch.object(mock, "flush", wraps=mock.flush) as flush_spy:
            with agent.session(session_id="s1", auto_flush=False) as s:
                s.track_user_message(content="hello")
            flush_spy.assert_not_called()

    @pytest.mark.asyncio
    async def test_async_session_flushes_when_auto_flush_true(self) -> None:
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")

        with patch.object(mock, "flush", wraps=mock.flush) as flush_spy:
            async with agent.session(session_id="s1", auto_flush=True) as s:
                s.track_user_message(content="hello")
            flush_spy.assert_called()

    def test_sync_session_flushes_on_exception(self) -> None:
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")

        with patch.object(mock, "flush", wraps=mock.flush) as flush_spy:
            with pytest.raises(RuntimeError, match="test error"):
                with agent.session(session_id="s1", auto_flush=True) as s:
                    s.track_user_message(content="hello")
                    raise RuntimeError("test error")
            flush_spy.assert_called()

    def test_session_end_tracked_before_flush(self) -> None:
        mock = MockAmplitudeAI()
        agent = mock.agent("bot", user_id="u1")

        call_order: list[str] = []
        original_flush = mock.flush

        def tracking_flush() -> list:
            call_order.append("flush")
            return original_flush()

        with patch.object(mock, "flush", side_effect=tracking_flush):
            with agent.session(session_id="s1", auto_flush=True) as s:
                s.track_user_message(content="hello")
                call_order.append("tracked")

        assert "tracked" in call_order
        assert "flush" in call_order
        assert call_order.index("flush") > call_order.index("tracked")
