#!/usr/bin/env python3
"""
Test Suite for Read-Aloud Service (Alt+R)
Tests on-demand text-to-speech functionality
"""

import os
import sys
import unittest
import tempfile
from unittest.mock import Mock, patch, MagicMock, call

# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))


class TestReadAloud(unittest.TestCase):
    """Test read-aloud service functionality"""

    def test_can_import_read_aloud(self):
        """Test that read-aloud service can be imported"""
        try:
            from dictation_service import read_aloud
            self.assertTrue(hasattr(read_aloud, 'MiddleClickReader'))
            self.assertTrue(hasattr(read_aloud, 'main'))
        except ImportError as e:
            self.fail(f"Cannot import read-aloud service: {e}")

    @patch('subprocess.run')
    def test_get_selected_text(self, mock_run):
        """Test getting selected text from xclip"""
        from dictation_service.read_aloud import MiddleClickReader

        reader = MiddleClickReader()

        # Mock xclip returning selected text
        mock_run.return_value = Mock(returncode=0, stdout="Hello World")
        result = reader.get_selected_text()

        # Verify xclip was called correctly
        mock_run.assert_called_once()
        call_args = mock_run.call_args
        self.assertIn('xclip', call_args[0][0])
        self.assertIn('primary', call_args[0][0])

    @patch('subprocess.run')
    @patch('tempfile.NamedTemporaryFile')
    @patch('os.path.exists')
    @patch('os.remove')
    def test_read_text(self, mock_remove, mock_exists, mock_temp, mock_run):
        """Test reading text with edge-tts"""
        from dictation_service.read_aloud import MiddleClickReader

        reader = MiddleClickReader()

        # Setup mocks
        mock_temp_file = MagicMock()
        mock_temp_file.name = '/tmp/test.mp3'
        mock_temp.__enter__ = Mock(return_value=mock_temp_file)
        mock_temp.__exit__ = Mock(return_value=False)
        mock_exists.return_value = True
        mock_run.return_value = Mock(returncode=0)

        # Test reading text
        reader.read_text("Hello World")

        # Verify TTS was called
        self.assertTrue(mock_run.called)

        # Check that edge-tts command was used
        calls = [call[0][0] for call in mock_run.call_args_list]
        edge_tts_called = any('edge-tts' in str(cmd) for cmd in calls)
        self.assertTrue(edge_tts_called or mock_run.called)

    def test_minimum_text_length(self):
        """Test that short text is not read"""
        from dictation_service.read_aloud import MiddleClickReader

        reader = MiddleClickReader()

        with patch('subprocess.run') as mock_run:
            # Text too short should not trigger TTS
            reader.read_text("a")
            reader.read_text("")

            # Should not have called edge-tts
            # (only xclip might be called)
            edge_tts_calls = [
                call for call in mock_run.call_args_list
                if 'edge-tts' in str(call)
            ]
            self.assertEqual(len(edge_tts_calls), 0)

    def test_lock_file_creation(self):
        """Test that lock file is created during reading"""
        from dictation_service.read_aloud import LOCK_FILE

        # Verify lock file path
        self.assertEqual(LOCK_FILE, "/tmp/dictation_speaking.lock")

    @patch('pynput.mouse.Listener')
    def test_mouse_listener_initialization(self, mock_listener):
        """Test that mouse listener can be initialized"""
        from dictation_service.read_aloud import MiddleClickReader

        reader = MiddleClickReader()

        # Mock listener
        mock_listener_instance = MagicMock()
        mock_listener.return_value.__enter__ = Mock(return_value=mock_listener_instance)
        mock_listener.return_value.__exit__ = Mock(return_value=False)

        # This would normally block, so we just test initialization
        self.assertIsNotNone(reader)

    def test_middle_click_detection(self):
        """Test middle-click detection logic"""
        from dictation_service.read_aloud import MiddleClickReader
        from pynput import mouse

        reader = MiddleClickReader()
        reader.ctrl_pressed = True  # Simulate Ctrl being held

        with patch.object(reader, 'get_selected_text', return_value="Test text"):
            with patch.object(reader, 'read_text') as mock_read:
                # Simulate Ctrl+middle-click press
                reader.on_click(100, 100, mouse.Button.middle, True)

                # Should have called read_text (in a thread, so wait a moment)
                import time
                time.sleep(0.1)
                mock_read.assert_called_once_with("Test text")

    def test_ignores_non_middle_clicks(self):
        """Test that non-middle clicks are ignored"""
        from dictation_service.read_aloud import MiddleClickReader
        from pynput import mouse

        reader = MiddleClickReader()

        with patch.object(reader, 'get_selected_text') as mock_get:
            with patch.object(reader, 'read_text') as mock_read:
                # Simulate left click
                reader.on_click(100, 100, mouse.Button.left, True)

                # Should not have called get_selected_text or read_text
                mock_get.assert_not_called()
                mock_read.assert_not_called()

    def test_concurrent_reading_prevention(self):
        """Test that concurrent reading is prevented"""
        from dictation_service.read_aloud import MiddleClickReader

        reader = MiddleClickReader()

        # Set reading flag
        reader.is_reading = True

        with patch('subprocess.run') as mock_run:
            # Try to read while already reading
            reader.read_text("Test text")

            # Should not have called subprocess
            mock_run.assert_not_called()


class TestEdgeTTSIntegration(unittest.TestCase):
    """Test Edge-TTS integration"""

    @patch('subprocess.run')
    def test_edge_tts_voice_configuration(self, mock_run):
        """Test that correct voice is used"""
        from dictation_service.read_aloud import EDGE_TTS_VOICE

        # Verify default voice
        self.assertEqual(EDGE_TTS_VOICE, "en-US-ChristopherNeural")

    @patch('subprocess.run')
    def test_mpv_playback(self, mock_run):
        """Test that mpv is used for playback"""
        from dictation_service.read_aloud import MiddleClickReader

        reader = MiddleClickReader()
        reader.is_reading = False

        with patch('tempfile.NamedTemporaryFile') as mock_temp:
            mock_temp_file = MagicMock()
            mock_temp_file.name = '/tmp/test.mp3'
            mock_temp.return_value.__enter__ = Mock(return_value=mock_temp_file)
            mock_temp.return_value.__exit__ = Mock(return_value=False)

            with patch('os.path.exists', return_value=True):
                with patch('os.remove'):
                    mock_run.return_value = Mock(returncode=0)

                    reader.read_text("Test text")

                    # Check that mpv was called
                    calls = [str(call) for call in mock_run.call_args_list]
                    mpv_called = any('mpv' in call for call in calls)
                    self.assertTrue(mpv_called or mock_run.called)


if __name__ == '__main__':
    unittest.main()
