#!/usr/bin/env python3
"""
End-to-End Test Suite for Dictation Service
Tests the complete dictation pipeline from keybindings to audio processing
"""

import os
import sys
import time
import subprocess
import tempfile
import threading
import queue
import json
from pathlib import Path

try:
    import sounddevice as sd
    import numpy as np
    from vosk import Model, KaldiRecognizer

    AUDIO_DEPS_AVAILABLE = True
except ImportError:
    AUDIO_DEPS_AVAILABLE = False

# Test configuration
TEST_DIR = Path("/mnt/storage/Development/dictation-service")
LOCK_FILES = {
    "dictation": TEST_DIR / "listening.lock",
    "conversation": TEST_DIR / "conversation.lock",
}


class DictationServiceTester:
    def __init__(self):
        self.results = []
        self.errors = []

    def log(self, message, level="INFO"):
        """Log test results"""
        timestamp = time.strftime("%H:%M:%S")
        print(f"[{timestamp}] {level}: {message}")
        self.results.append(f"{level}: {message}")

    def error(self, message):
        """Log errors"""
        self.log(message, "ERROR")
        self.errors.append(message)

    def test_lock_file_operations(self):
        """Test 1: Lock file creation and removal"""
        self.log("Testing lock file operations...")

        # Test dictation lock
        dictation_lock = LOCK_FILES["dictation"]

        # Ensure clean state
        if dictation_lock.exists():
            dictation_lock.unlink()

        # Test creation
        dictation_lock.touch()
        if dictation_lock.exists():
            self.log("✓ Dictation lock file creation works")
        else:
            self.error("✗ Dictation lock file creation failed")

        # Test removal
        dictation_lock.unlink()
        if not dictation_lock.exists():
            self.log("✓ Dictation lock file removal works")
        else:
            self.error("✗ Dictation lock file removal failed")

        # Test conversation lock
        conv_lock = LOCK_FILES["conversation"]

        # Ensure clean state
        if conv_lock.exists():
            conv_lock.unlink()

        # Test creation
        conv_lock.touch()
        if conv_lock.exists():
            self.log("✓ Conversation lock file creation works")
        else:
            self.error("✗ Conversation lock file creation failed")

        conv_lock.unlink()

    def test_toggle_scripts(self):
        """Test 2: Toggle script functionality"""
        self.log("Testing toggle scripts...")

        # Test dictation toggle
        toggle_script = TEST_DIR / "scripts" / "toggle-dictation.sh"

        # Ensure clean state
        if LOCK_FILES["dictation"].exists():
            LOCK_FILES["dictation"].unlink()

        # Run toggle script
        result = subprocess.run([str(toggle_script)], capture_output=True, text=True)
        if result.returncode == 0:
            self.log("✓ Dictation toggle script executed successfully")
            if LOCK_FILES["dictation"].exists():
                self.log("✓ Dictation lock file created by script")
            else:
                self.error("✗ Dictation lock file not created by script")
        else:
            self.error(f"✗ Dictation toggle script failed: {result.stderr}")

        # Toggle again to remove lock
        result = subprocess.run([str(toggle_script)], capture_output=True, text=True)
        if result.returncode == 0 and not LOCK_FILES["dictation"].exists():
            self.log("✓ Dictation toggle script properly removes lock file")
        else:
            self.error("✗ Dictation toggle script failed to remove lock file")

    def test_service_status(self):
        """Test 3: Service status and responsiveness"""
        self.log("Testing service status...")

        # Check if dictation service is running
        result = subprocess.run(
            ["systemctl", "--user", "is-active", "dictation.service"],
            capture_output=True,
            text=True,
        )
        if result.returncode == 0 and result.stdout.strip() == "active":
            self.log("✓ Dictation service is active")
        else:
            self.error(f"✗ Dictation service not active: {result.stdout.strip()}")

        # Check keybinding listener service
        result = subprocess.run(
            ["systemctl", "--user", "is-active", "keybinding-listener.service"],
            capture_output=True,
            text=True,
        )
        if result.returncode == 0 and result.stdout.strip() == "active":
            self.log("✓ Keybinding listener service is active")
        else:
            self.error(
                f"✗ Keybinding listener service not active: {result.stdout.strip()}"
            )

    def test_audio_devices(self):
        """Test 4: Audio device availability"""
        self.log("Testing audio devices...")

        if not AUDIO_DEPS_AVAILABLE:
            self.error("✗ Audio dependencies not available")
            return

        try:
            devices = sd.query_devices()
            input_devices = []

            # Handle different sounddevice API versions
            if isinstance(devices, list):
                for i, device in enumerate(devices):
                    try:
                        if (
                            hasattr(device, "get")
                            and device.get("max_input_channels", 0) > 0
                        ):
                            input_devices.append(device)
                        elif (
                            hasattr(device, "__getitem__")
                            and len(device) > 2
                            and device[2] > 0
                        ):
                            input_devices.append(device)
                    except:
                        continue

            if input_devices:
                self.log(f"✓ Found {len(input_devices)} audio input device(s)")
                try:
                    default_input = sd.query_devices(kind="input")
                    if default_input:
                        device_name = (
                            default_input.get("name", "Unknown")
                            if hasattr(default_input, "get")
                            else str(default_input)
                        )
                        self.log(f"✓ Default input device available")
                    else:
                        self.error("✗ No default input device found")
                except:
                    self.log("✓ Audio devices found (default device check skipped)")
            else:
                self.error("✗ No audio input devices found")

        except Exception as e:
            self.error(f"✗ Audio device test failed: {e}")

    def test_vosk_model(self):
        """Test 5: Vosk model loading and recognition"""
        self.log("Testing Vosk model...")

        if not AUDIO_DEPS_AVAILABLE:
            self.error("✗ Audio dependencies not available for Vosk testing")
            return

        try:
            model_path = (
                TEST_DIR / "src" / "dictation_service" / "vosk-model-small-en-us-0.15"
            )
            if model_path.exists():
                self.log("✓ Vosk model directory exists")

                # Try to load model
                model = Model(str(model_path))
                self.log("✓ Vosk model loaded successfully")

                # Test recognizer
                rec = KaldiRecognizer(model, 16000)
                self.log("✓ Vosk recognizer created successfully")

                # Test with dummy audio data
                dummy_audio = np.random.randint(-32768, 32767, 1600, dtype=np.int16)
                if rec.AcceptWaveform(dummy_audio.tobytes()):
                    result = json.loads(rec.Result())
                    self.log(
                        f"✓ Vosk recognition test passed: {result.get('text', 'no text')}"
                    )
                else:
                    self.log("✓ Vosk recognition accepts audio data")

            else:
                self.error("✗ Vosk model directory not found")

        except Exception as e:
            self.error(f"✗ Vosk model test failed: {e}")

    def test_keybinding_simulation(self):
        """Test 6: Keybinding simulation"""
        self.log("Testing keybinding simulation...")

        # Test direct script execution
        toggle_script = TEST_DIR / "scripts" / "toggle-dictation.sh"

        # Clean state
        if LOCK_FILES["dictation"].exists():
            LOCK_FILES["dictation"].unlink()

        # Simulate keybinding by running script
        result = subprocess.run(
            [str(toggle_script)],
            capture_output=True,
            text=True,
            env={"DISPLAY": ":1", "XAUTHORITY": "/run/user/1000/gdm/Xauthority"},
        )

        if result.returncode == 0:
            self.log("✓ Keybinding simulation (script execution) works")
            if LOCK_FILES["dictation"].exists():
                self.log("✓ Lock file created via simulated keybinding")
            else:
                self.error("✗ Lock file not created via simulated keybinding")
        else:
            self.error(f"✗ Keybinding simulation failed: {result.stderr}")

    def test_service_logs(self):
        """Test 7: Check service logs for errors"""
        self.log("Checking service logs...")

        # Check dictation service logs
        result = subprocess.run(
            [
                "journalctl",
                "--user",
                "-u",
                "dictation.service",
                "-n",
                "10",
                "--no-pager",
            ],
            capture_output=True,
            text=True,
        )
        if "error" in result.stdout.lower() or "exception" in result.stdout.lower():
            self.error("✗ Errors found in dictation service logs")
            self.log(f"Log excerpt: {result.stdout[-500:]}")
        else:
            self.log("✓ No obvious errors in dictation service logs")

        # Check keybinding listener logs
        result = subprocess.run(
            [
                "journalctl",
                "--user",
                "-u",
                "keybinding-listener.service",
                "-n",
                "10",
                "--no-pager",
            ],
            capture_output=True,
            text=True,
        )
        if "error" in result.stdout.lower() or "exception" in result.stdout.lower():
            self.error("✗ Errors found in keybinding listener logs")
            self.log(f"Log excerpt: {result.stdout[-500:]}")
        else:
            self.log("✓ No obvious errors in keybinding listener logs")

    def test_end_to_end_flow(self):
        """Test 8: End-to-end dictation flow"""
        self.log("Testing end-to-end dictation flow...")

        # This is a simplified e2e test - in a real scenario we'd need to:
        # 1. Start dictation mode
        # 2. Send audio data
        # 3. Check if text is generated
        # 4. Stop dictation mode

        # For now, just test the basic flow
        self.log("Note: Full e2e audio processing test requires manual testing")
        self.log("Basic components tested above should enable manual e2e testing")

    def run_all_tests(self):
        """Run all tests"""
        self.log("Starting Dictation Service E2E Test Suite")
        self.log("=" * 50)

        test_methods = [
            self.test_lock_file_operations,
            self.test_toggle_scripts,
            self.test_service_status,
            self.test_audio_devices,
            self.test_vosk_model,
            self.test_keybinding_simulation,
            self.test_service_logs,
            self.test_end_to_end_flow,
        ]

        for test_method in test_methods:
            try:
                test_method()
                self.log("-" * 30)
            except Exception as e:
                self.error(f"Test {test_method.__name__} crashed: {e}")
                self.log("-" * 30)

        # Summary
        self.log("=" * 50)
        self.log("TEST SUMMARY")
        self.log(f"Total tests: {len(test_methods)}")
        self.log(f"Errors: {len(self.errors)}")

        if self.errors:
            self.log("FAILED TESTS:")
            for error in self.errors:
                self.log(f"  - {error}")
            return False
        else:
            self.log("ALL TESTS PASSED ✓")
            return True


def main():
    tester = DictationServiceTester()
    success = tester.run_all_tests()

    # Print full results
    print("\n" + "=" * 50)
    print("FULL TEST RESULTS:")
    for result in tester.results:
        print(result)

    return 0 if success else 1


if __name__ == "__main__":
    sys.exit(main())
