"""
Unit tests for SentenceStore service.

Tests mock Supabase interactions and verify graceful degradation when env vars missing.
"""

import pytest
from unittest.mock import MagicMock, patch, AsyncMock
from datetime import datetime, timezone
import asyncio


class TestSentenceStoreInit:
    """Test SentenceStore initialization and configuration."""
    
    def test_init_without_env_vars(self):
        """SentenceStore should initialize gracefully without env vars."""
        with patch.dict('os.environ', {}, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            assert store.is_configured() is False
            assert store.client is None
    
    def test_init_with_url_only(self):
        """SentenceStore should not be configured with URL only (needs service key)."""
        with patch.dict('os.environ', {'SUPABASE_URL': 'https://test.supabase.co'}, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            assert store.is_configured() is False
    
    def test_init_with_key_only(self):
        """SentenceStore should not be configured with key only (needs URL)."""
        with patch.dict('os.environ', {'SUPABASE_SERVICE_KEY': 'test-key'}, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            assert store.is_configured() is False
    
    @patch('veena3modal.services.sentence_store.create_client')
    def test_init_with_all_env_vars(self, mock_create_client):
        """SentenceStore should be configured with both URL and service key."""
        mock_client = MagicMock()
        mock_create_client.return_value = mock_client
        
        with patch.dict('os.environ', {
            'SUPABASE_URL': 'https://test.supabase.co',
            'SUPABASE_SERVICE_KEY': 'test-service-key'
        }, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            assert store.is_configured() is True
            mock_create_client.assert_called_once_with(
                'https://test.supabase.co',
                'test-service-key'
            )
    
    @patch('veena3modal.services.sentence_store.create_client')
    def test_init_with_alternative_env_vars(self, mock_create_client):
        """SentenceStore should accept alternative env var names (SUPABASE_KEY)."""
        mock_client = MagicMock()
        mock_create_client.return_value = mock_client
        
        with patch.dict('os.environ', {
            'SUPABASE_URL': 'https://test.supabase.co',
            'SUPABASE_KEY': 'test-anon-key'  # Alternative name
        }, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            assert store.is_configured() is True


class TestSentenceStoreRecord:
    """Test SentenceStore record creation."""
    
    def test_create_record_dict(self):
        """Test creating a record dictionary with all fields."""
        from veena3modal.services.sentence_store import create_sentence_record
        
        record = create_sentence_record(
            request_id="test-req-123",
            text="Hello world, this is a test.",
            speaker="reet",
            stream=True,
            format="wav",
            temperature=0.8,
            top_k=50,
            top_p=1.0,
            max_tokens=4096,
            repetition_penalty=1.05,
            seed=42,
            text_chunked=False,
            ttfb_ms=150,
            audio_duration_seconds=2.5,
        )
        
        assert record["request_id"] == "test-req-123"
        assert record["text"] == "Hello world, this is a test."
        assert record["speaker"] == "reet"
        assert record["stream"] is True
        assert record["format"] == "wav"
        assert record["temperature"] == 0.8
        assert record["top_k"] == 50
        assert record["top_p"] == 1.0
        assert record["max_tokens"] == 4096
        assert record["repetition_penalty"] == 1.05
        assert record["seed"] == 42
        assert record["text_length"] == 28
        assert record["text_chunked"] is False
        assert record["ttfb_ms"] == 150
        assert record["audio_duration_seconds"] == 2.5
        assert "created_at" in record
    
    def test_create_record_minimal(self):
        """Test creating a record with minimal fields."""
        from veena3modal.services.sentence_store import create_sentence_record
        
        record = create_sentence_record(
            request_id="min-req-456",
            text="Short text",
            speaker="lipakshi",
        )
        
        assert record["request_id"] == "min-req-456"
        assert record["text"] == "Short text"
        assert record["speaker"] == "lipakshi"
        assert record["text_length"] == 10
        # Defaults
        assert record["stream"] is False
        assert record["format"] == "wav"
        assert record["seed"] is None
        assert record["ttfb_ms"] is None
    
    def test_create_record_unicode_text(self):
        """Test record creation with Unicode/Hindi text."""
        from veena3modal.services.sentence_store import create_sentence_record
        
        hindi_text = "नमस्ते, यह एक परीक्षण है।"
        record = create_sentence_record(
            request_id="unicode-req",
            text=hindi_text,
            speaker="mitra",
        )
        
        assert record["text"] == hindi_text
        assert record["text_length"] == len(hindi_text)
    
    def test_create_record_long_text(self):
        """Test record creation with long text (50K chars max)."""
        from veena3modal.services.sentence_store import create_sentence_record
        
        long_text = "A" * 50000
        record = create_sentence_record(
            request_id="long-req",
            text=long_text,
            speaker="reet",
        )
        
        assert record["text_length"] == 50000


class TestSentenceStoreStore:
    """Test SentenceStore.store() method."""
    
    @pytest.mark.asyncio
    async def test_store_when_not_configured(self):
        """store() should return False when not configured."""
        with patch.dict('os.environ', {}, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            
            result = await store.store(
                request_id="test-123",
                text="Test text",
                speaker="reet",
            )
            
            assert result is False
    
    @pytest.mark.asyncio
    @patch('veena3modal.services.sentence_store.create_client')
    async def test_store_success(self, mock_create_client):
        """store() should insert record into Supabase."""
        # Setup mock
        mock_table = MagicMock()
        mock_table.insert.return_value.execute.return_value = MagicMock(data=[{"id": "new-id"}])
        mock_client = MagicMock()
        mock_client.table.return_value = mock_table
        mock_create_client.return_value = mock_client
        
        with patch.dict('os.environ', {
            'SUPABASE_URL': 'https://test.supabase.co',
            'SUPABASE_SERVICE_KEY': 'test-key'
        }, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            
            result = await store.store(
                request_id="test-123",
                text="Hello world",
                speaker="reet",
                stream=True,
                format="wav",
            )
            
            assert result is True
            mock_client.table.assert_called_with('tts_requests')
            mock_table.insert.assert_called_once()
    
    @pytest.mark.asyncio
    @patch('veena3modal.services.sentence_store.create_client')
    async def test_store_handles_exception(self, mock_create_client):
        """store() should handle exceptions gracefully."""
        mock_table = MagicMock()
        mock_table.insert.return_value.execute.side_effect = Exception("DB error")
        mock_client = MagicMock()
        mock_client.table.return_value = mock_table
        mock_create_client.return_value = mock_client
        
        with patch.dict('os.environ', {
            'SUPABASE_URL': 'https://test.supabase.co',
            'SUPABASE_SERVICE_KEY': 'test-key'
        }, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            
            # Should not raise, just return False
            result = await store.store(
                request_id="test-123",
                text="Hello world",
                speaker="reet",
            )
            
            assert result is False


class TestSentenceStoreFireAndForget:
    """Test non-blocking fire-and-forget storage."""
    
    @pytest.mark.asyncio
    async def test_store_fire_and_forget_when_not_configured(self):
        """fire_and_forget should do nothing when not configured."""
        with patch.dict('os.environ', {}, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            
            # Should not raise, should return immediately
            task = store.store_fire_and_forget(
                request_id="test-123",
                text="Test text",
                speaker="reet",
            )
            
            # Should return None (no task created)
            assert task is None
    
    @pytest.mark.asyncio
    @patch('veena3modal.services.sentence_store.create_client')
    async def test_store_fire_and_forget_creates_task(self, mock_create_client):
        """fire_and_forget should create a background task."""
        mock_table = MagicMock()
        mock_table.insert.return_value.execute.return_value = MagicMock(data=[{"id": "new-id"}])
        mock_client = MagicMock()
        mock_client.table.return_value = mock_table
        mock_create_client.return_value = mock_client
        
        with patch.dict('os.environ', {
            'SUPABASE_URL': 'https://test.supabase.co',
            'SUPABASE_SERVICE_KEY': 'test-key'
        }, clear=True):
            from veena3modal.services.sentence_store import SentenceStore
            store = SentenceStore()
            
            task = store.store_fire_and_forget(
                request_id="test-123",
                text="Test text",
                speaker="reet",
            )
            
            # Should return a task
            assert task is not None
            assert isinstance(task, asyncio.Task)
            
            # Wait for task to complete
            await task
            
            # Verify insert was called
            mock_table.insert.assert_called_once()


class TestSentenceStoreSingleton:
    """Test singleton pattern for SentenceStore."""
    
    def test_get_sentence_store_returns_same_instance(self):
        """get_sentence_store should return the same instance."""
        from veena3modal.services.sentence_store import get_sentence_store, _reset_singleton
        
        # Reset for clean test
        _reset_singleton()
        
        with patch.dict('os.environ', {}, clear=True):
            store1 = get_sentence_store()
            store2 = get_sentence_store()
            
            assert store1 is store2
            
            # Cleanup
            _reset_singleton()
    
    def test_is_sentence_store_configured(self):
        """is_sentence_store_configured should reflect singleton state."""
        from veena3modal.services.sentence_store import (
            get_sentence_store, 
            is_sentence_store_configured,
            _reset_singleton
        )
        
        _reset_singleton()
        
        with patch.dict('os.environ', {}, clear=True):
            # Before getting store
            assert is_sentence_store_configured() is False
            
            # After getting store (still not configured due to missing env)
            get_sentence_store()
            assert is_sentence_store_configured() is False
            
            _reset_singleton()

