from datetime import datetime
from unittest.mock import Mock, MagicMock
import pytest

from strands.session.session_repository import Session, SessionAgent, SessionMessage
from supabase_strands import SupabaseSessionManager


@pytest.fixture
def mock_supabase_client(monkeypatch):
    """Mock Supabase client for unit tests."""
    mock_client = MagicMock()
    mock_create_client = Mock(return_value=mock_client)
    monkeypatch.setattr("supabase_strands.session_manager.create_client", mock_create_client)
    return mock_client


@pytest.fixture
def session_manager(mock_supabase_client, monkeypatch):
    """Create session manager with mocked client."""
    monkeypatch.setenv("SUPABASE_URL", "https://test.supabase.co")
    monkeypatch.setenv("SUPABASE_KEY", "test-key")
    return SupabaseSessionManager(session_id="test-session")


def test_init_with_env_vars(monkeypatch):
    """Test initialization with environment variables."""
    monkeypatch.setenv("SUPABASE_URL", "https://test.supabase.co")
    monkeypatch.setenv("SUPABASE_KEY", "test-key")
    mock_client = MagicMock()
    mock_create = Mock(return_value=mock_client)
    monkeypatch.setattr("supabase_strands.session_manager.create_client", mock_create)
    # read_session returns no data so RepositorySessionManager creates a new session
    mock_client.table.return_value.select.return_value.eq.return_value.execute.return_value.data = []
    mock_client.table.return_value.insert.return_value.execute.return_value = None

    manager = SupabaseSessionManager(session_id="test")
    mock_create.assert_called_once_with("https://test.supabase.co", "test-key")


def test_init_with_params(monkeypatch):
    """Test initialization with explicit parameters."""
    mock_client = MagicMock()
    mock_create = Mock(return_value=mock_client)
    monkeypatch.setattr("supabase_strands.session_manager.create_client", mock_create)
    mock_client.table.return_value.select.return_value.eq.return_value.execute.return_value.data = []
    mock_client.table.return_value.insert.return_value.execute.return_value = None
    
    manager = SupabaseSessionManager(
        session_id="test",
        supabase_url="https://custom.supabase.co",
        supabase_key="custom-key"
    )
    mock_create.assert_called_once_with("https://custom.supabase.co", "custom-key")


def test_init_missing_credentials():
    """Test initialization fails without credentials."""
    with pytest.raises(ValueError, match="Supabase credentials required"):
        SupabaseSessionManager(session_id="test")


def test_create_session(session_manager, mock_supabase_client):
    """Test session creation."""
    session = Session(
        session_id="test-session",
        session_type="AGENT",
        created_at=datetime.utcnow().isoformat(),
        updated_at=datetime.utcnow().isoformat(),
    )

    mock_supabase_client.table.return_value.insert.return_value.execute.return_value = None
    result = session_manager.create_session(session)

    assert result == session
    mock_supabase_client.table.assert_called_with("sessions")


def test_read_session(session_manager, mock_supabase_client):
    """Test session retrieval."""
    mock_data = {
        "session_id": "test-session",
        "session_type": "AGENT",
        "created_at": "2024-01-01T00:00:00",
        "updated_at": "2024-01-01T00:00:00",
    }
    mock_supabase_client.table.return_value.select.return_value.eq.return_value.execute.return_value.data = [mock_data]
    
    result = session_manager.read_session("test-session")
    
    assert result is not None
    assert result.session_id == "test-session"
    assert result.session_type == "AGENT"


def test_read_session_not_found(session_manager, mock_supabase_client):
    """Test session retrieval when not found."""
    mock_supabase_client.table.return_value.select.return_value.eq.return_value.execute.return_value.data = []
    
    result = session_manager.read_session("nonexistent")
    assert result is None


def test_delete_session(session_manager, mock_supabase_client):
    """Test session deletion."""
    mock_supabase_client.table.return_value.delete.return_value.eq.return_value.execute.return_value = None
    
    session_manager.delete_session("test-session")
    mock_supabase_client.table.assert_called_with("sessions")


def test_create_agent(session_manager, mock_supabase_client):
    """Test agent creation."""
    agent = SessionAgent(
        agent_id="agent-1",
        state={"key": "value"},
        conversation_manager_state={},
        _internal_state={},
        created_at=datetime.utcnow().isoformat(),
        updated_at=datetime.utcnow().isoformat(),
    )

    mock_supabase_client.table.return_value.insert.return_value.execute.return_value = None
    result = session_manager.create_agent("test-session", agent)

    assert result == agent
    mock_supabase_client.table.assert_called_with("agents")


def test_read_agent(session_manager, mock_supabase_client):
    """Test agent retrieval."""
    mock_data = {
        "agent_id": "agent-1",
        "state": {"key": "value"},
        "conversation_manager_state": {},
        "_internal_state": {},
        "created_at": "2024-01-01T00:00:00",
        "updated_at": "2024-01-01T00:00:00",
    }
    mock_supabase_client.table.return_value.select.return_value.eq.return_value.eq.return_value.execute.return_value.data = [mock_data]
    
    result = session_manager.read_agent("test-session", "agent-1")
    
    assert result is not None
    assert result.agent_id == "agent-1"
    assert result.state == {"key": "value"}


def test_update_agent(session_manager, mock_supabase_client):
    """Test agent update."""
    agent = SessionAgent(
        agent_id="agent-1",
        state={"updated": "value"},
        conversation_manager_state={},
        _internal_state={},
        created_at=datetime.utcnow().isoformat(),
        updated_at=datetime.utcnow().isoformat(),
    )

    mock_supabase_client.table.return_value.update.return_value.eq.return_value.eq.return_value.execute.return_value = None
    result = session_manager.update_agent("test-session", agent)

    assert result == agent


def test_create_message(session_manager, mock_supabase_client):
    """Test message creation."""
    message = SessionMessage(
        message_id=0,
        message={"role": "user", "content": "Hello"},
        redact_message=None,
        created_at=datetime.utcnow().isoformat(),
        updated_at=datetime.utcnow().isoformat(),
    )

    mock_supabase_client.table.return_value.insert.return_value.execute.return_value = None
    result = session_manager.create_message("test-session", "agent-1", message)

    assert result == message
    mock_supabase_client.table.assert_called_with("messages")


def test_read_message(session_manager, mock_supabase_client):
    """Test message retrieval."""
    mock_data = {
        "message_id": 0,
        "message": {"role": "user", "content": "Hello"},
        "redact_message": None,
        "created_at": "2024-01-01T00:00:00",
        "updated_at": "2024-01-01T00:00:00",
    }
    mock_supabase_client.table.return_value.select.return_value.eq.return_value.eq.return_value.eq.return_value.execute.return_value.data = [mock_data]
    
    result = session_manager.read_message("test-session", "agent-1", 0)
    
    assert result is not None
    assert result.message_id == 0
    assert result.message["content"] == "Hello"


def test_list_messages(session_manager, mock_supabase_client):
    """Test listing messages."""
    mock_data = [
        {
            "message_id": 0,
            "message": {"role": "user", "content": "Hello"},
            "redact_message": None,
            "created_at": "2024-01-01T00:00:00",
            "updated_at": "2024-01-01T00:00:00",
        },
        {
            "message_id": 1,
            "message": {"role": "assistant", "content": "Hi"},
            "redact_message": None,
            "created_at": "2024-01-01T00:00:01",
            "updated_at": "2024-01-01T00:00:01",
        },
    ]
    mock_supabase_client.table.return_value.select.return_value.eq.return_value.eq.return_value.order.return_value.range.return_value.execute.return_value.data = mock_data
    
    result = session_manager.list_messages("test-session", "agent-1")
    
    assert len(result) == 2
    assert result[0].message_id == 0
    assert result[1].message_id == 1
