"""Tests for cortex/hippo/tools/conversation_scope.py."""

from __future__ import annotations

from datetime import datetime, timezone
from unittest.mock import AsyncMock, MagicMock

import pytest
import pytest_asyncio

from cortex.hippo.tools.conversation_scope import (
    LcmConversationScope,
    parse_iso_timestamp_param,
    resolve_conversation_scope,
)


class TestParseIsoTimestampParam:
    def test_valid_utc(self):
        params = {"since": "2026-03-15T10:30:00Z"}
        result = parse_iso_timestamp_param(params, "since")
        assert result is not None
        assert result.year == 2026
        assert result.month == 3
        assert result.day == 15
        assert result.hour == 10
        assert result.minute == 30

    def test_valid_with_offset(self):
        params = {"before": "2026-03-15T10:30:00+07:00"}
        result = parse_iso_timestamp_param(params, "before")
        assert result is not None
        assert result.year == 2026

    def test_missing_param(self):
        assert parse_iso_timestamp_param({}, "since") is None

    def test_non_string_param(self):
        assert parse_iso_timestamp_param({"since": 12345}, "since") is None

    def test_empty_string(self):
        assert parse_iso_timestamp_param({"since": "  "}, "since") is None

    def test_invalid_timestamp_raises(self):
        with pytest.raises(ValueError, match="since must be a valid ISO timestamp"):
            parse_iso_timestamp_param({"since": "not-a-date"}, "since")

    def test_none_value(self):
        assert parse_iso_timestamp_param({"since": None}, "since") is None


class TestResolveConversationScope:
    @pytest.mark.asyncio
    async def test_explicit_conversation_id(self):
        engine = MagicMock()
        result = await resolve_conversation_scope(
            engine, {"conversationId": 42}
        )
        assert result.conversation_id == 42
        assert result.all_conversations is False

    @pytest.mark.asyncio
    async def test_explicit_float_conversation_id(self):
        engine = MagicMock()
        result = await resolve_conversation_scope(
            engine, {"conversationId": 42.0}
        )
        assert result.conversation_id == 42
        assert result.all_conversations is False

    @pytest.mark.asyncio
    async def test_all_conversations(self):
        engine = MagicMock()
        result = await resolve_conversation_scope(
            engine, {"allConversations": True}
        )
        assert result.conversation_id is None
        assert result.all_conversations is True

    @pytest.mark.asyncio
    async def test_from_session_id(self):
        mock_conv = MagicMock()
        mock_conv.conversation_id = 99

        mock_store = AsyncMock()
        mock_store.get_or_create_conversation.return_value = mock_conv

        engine = MagicMock()
        engine.conversation_store = mock_store
        engine.owner_id = "test-owner"

        result = await resolve_conversation_scope(
            engine, {}, session_id="session-123"
        )
        assert result.conversation_id == 99
        assert result.all_conversations is False

    @pytest.mark.asyncio
    async def test_from_session_key(self):
        mock_conv = MagicMock()
        mock_conv.conversation_id = 77

        mock_store = AsyncMock()
        mock_store.get_or_create_conversation.return_value = mock_conv

        engine = MagicMock()
        engine.conversation_store = mock_store
        engine.owner_id = "test-owner"

        async def resolve_key(key: str):
            return "resolved-session-id"

        result = await resolve_conversation_scope(
            engine,
            {},
            session_key="some-key",
            resolve_session_id_from_key=resolve_key,
        )
        assert result.conversation_id == 77
        assert result.all_conversations is False

    @pytest.mark.asyncio
    async def test_fallback_no_session(self):
        engine = MagicMock()
        result = await resolve_conversation_scope(engine, {})
        assert result.conversation_id is None
        assert result.all_conversations is False

    @pytest.mark.asyncio
    async def test_conversation_id_takes_priority(self):
        """Explicit conversationId wins over allConversations."""
        engine = MagicMock()
        result = await resolve_conversation_scope(
            engine,
            {"conversationId": 10, "allConversations": True},
        )
        assert result.conversation_id == 10
        assert result.all_conversations is False
