#!/usr/bin/env python3
"""
Tests for Phase 8: CLI Embeddings - Semantic Search via novyx query command.

Tests cover:
1. Basic query stats functionality
2. Semantic search with various parameters
3. JSON output formatting
4. Error handling
5. Edge cases
"""

import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch, MagicMock
import sys

# Add project root to path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

from tools.cli import cmd_query


class TestCLIQueryStats(unittest.TestCase):
    """Test basic query statistics functionality."""
    
    def setUp(self):
        """Create temporary directory with test artifacts."""
        self.temp_dir = tempfile.TemporaryDirectory()
        self.test_dir = Path(self.temp_dir.name)
        
        # Create test artifacts
        self._create_test_artifact("test1.jsonld", {
            "@context": "https://schema.org/",
            "@type": "DigitalDocument",
            "uuid": "urn:uuid:test-001",
            "name": "Test Artifact 1",
            "createdAt": "2026-01-13T10:00:00Z",
            "category": "test",
            "articleBody": "Test content for artifact one"
        })
        
        self._create_test_artifact("test2.jsonld", {
            "@context": "https://schema.org/",
            "@type": "novyx:Decision",
            "uuid": "urn:uuid:test-002",
            "name": "Test Decision",
            "createdAt": "2026-01-13T11:00:00Z",
            "category": "decisions",
            "context": "Test decision context"
        })
    
    def tearDown(self):
        """Clean up temporary directory."""
        self.temp_dir.cleanup()
    
    def _create_test_artifact(self, filename, data):
        """Helper to create a test artifact file."""
        filepath = self.test_dir / filename
        with open(filepath, 'w') as f:
            json.dump(data, f, indent=2)
    
    def test_query_stats_basic(self):
        """Test basic statistics output."""
        args = MagicMock()
        args.directory = str(self.test_dir)
        args.verbose = False
        args.search = None
        
        result = cmd_query(args)
        self.assertEqual(result, 0)
    
    def test_query_stats_with_categories(self):
        """Test statistics categorization."""
        args = MagicMock()
        args.directory = str(self.test_dir)
        args.verbose = False
        args.search = None
        
        result = cmd_query(args)
        self.assertEqual(result, 0)
    
    def test_query_empty_directory(self):
        """Test query on empty directory."""
        empty_dir = self.test_dir / "empty"
        empty_dir.mkdir()
        
        args = MagicMock()
        args.directory = str(empty_dir)
        args.verbose = False
        args.search = None
        
        result = cmd_query(args)
        self.assertEqual(result, 0)
    
    def test_query_nonexistent_directory(self):
        """Test query on nonexistent directory."""
        args = MagicMock()
        args.directory = "/nonexistent/path"
        args.verbose = False
        args.search = None
        
        result = cmd_query(args)
        self.assertEqual(result, 1)
    
    def test_query_with_malformed_artifact(self):
        """Test query with malformed JSON artifact."""
        # Create malformed artifact
        bad_file = self.test_dir / "bad.jsonld"
        with open(bad_file, 'w') as f:
            f.write("{ invalid json }")
        
        args = MagicMock()
        args.directory = str(self.test_dir)
        args.verbose = False
        args.search = None
        
        # Should not crash, should skip bad file
        result = cmd_query(args)
        self.assertEqual(result, 0)


class TestCLISemanticSearch(unittest.TestCase):
    """Test semantic search functionality via CLI."""
    
    def setUp(self):
        """Create temporary directory with embedded test artifacts."""
        self.temp_dir = tempfile.TemporaryDirectory()
        self.test_dir = Path(self.temp_dir.name)
        
        # Create artifacts with mock embeddings
        self._create_embedded_artifact("doc1.jsonld", {
            "@context": "https://schema.org/",
            "@type": "DigitalDocument",
            "uuid": "urn:uuid:embed-001",
            "name": "Database Selection Guide",
            "createdAt": "2026-01-13T10:00:00Z",
            "category": "research",
            "articleBody": "Choosing between PostgreSQL and MongoDB for your application",
            "novyx:embedding": [0.1, 0.2, 0.3, 0.4, 0.5]  # Mock embedding
        })
        
        self._create_embedded_artifact("doc2.jsonld", {
            "@context": "https://schema.org/",
            "@type": "DigitalDocument",
            "uuid": "urn:uuid:embed-002",
            "name": "API Authentication Methods",
            "createdAt": "2026-01-13T11:00:00Z",
            "category": "security",
            "articleBody": "Comparing JWT and API keys for authentication",
            "novyx:embedding": [0.5, 0.4, 0.3, 0.2, 0.1]  # Mock embedding
        })
    
    def tearDown(self):
        """Clean up temporary directory."""
        self.temp_dir.cleanup()
    
    def _create_embedded_artifact(self, filename, data):
        """Helper to create artifact with embeddings."""
        filepath = self.test_dir / filename
        with open(filepath, 'w') as f:
            json.dump(data, f, indent=2)
    
    @patch('tools.query.ML_AVAILABLE', False)
    def test_semantic_search_no_dependencies(self):
        """Test semantic search when dependencies not available."""
        args = MagicMock()
        args.directory = str(self.test_dir)
        args.search = "database selection"
        args.min_score = 0.2
        args.top_k = 10
        args.json = False
        args.verbose = False
        
        # Should return error but not crash
        result = cmd_query(args)
        self.assertEqual(result, 1)
    
    def test_semantic_search_with_results(self):
        """Test semantic search returning results (graceful degradation without ML deps)."""
        args = MagicMock()
        args.directory = str(self.test_dir)
        args.search = "database"
        args.min_score = 0.2
        args.top_k = 10
        args.json = False
        args.verbose = False
        
        # Without ML dependencies, should return error but not crash
        result = cmd_query(args)
        # Result can be 0 or 1 depending on whether ML dependencies available
        self.assertIn(result, [0, 1])
    
    def test_semantic_search_json_output(self):
        """Test semantic search with JSON output format."""
        args = MagicMock()
        args.directory = str(self.test_dir)
        args.search = "authentication"
        args.min_score = 0.3
        args.top_k = 5
        args.json = True
        args.verbose = False
        
        # Should return JSON formatted results or error
        result = cmd_query(args)
        self.assertIn(result, [0, 1])
    
    def test_semantic_search_min_score_threshold(self):
        """Test semantic search with different min_score values."""
        for min_score in [0.1, 0.5, 0.9]:
            args = MagicMock()
            args.directory = str(self.test_dir)
            args.search = "test query"
            args.min_score = min_score
            args.top_k = 10
            args.json = False
            args.verbose = False
            
            result = cmd_query(args)
            self.assertIn(result, [0, 1])
    
    def test_semantic_search_top_k_parameter(self):
        """Test semantic search with different top_k values."""
        for top_k in [1, 5, 20]:
            args = MagicMock()
            args.directory = str(self.test_dir)
            args.search = "test query"
            args.min_score = 0.2
            args.top_k = top_k
            args.json = False
            args.verbose = False
            
            result = cmd_query(args)
            self.assertIn(result, [0, 1])


class TestQueryIntegration(unittest.TestCase):
    """Integration tests for query functionality."""
    
    def test_query_command_help(self):
        """Test that query command provides help."""
        # This is a smoke test to ensure the command is properly registered
        import subprocess
        result = subprocess.run(
            [sys.executable, '-m', 'tools.cli', 'query', '--help'],
            capture_output=True,
            text=True,
            cwd=project_root
        )
        
        self.assertEqual(result.returncode, 0)
        self.assertIn('semantic search', result.stdout.lower())
        self.assertIn('--min-score', result.stdout)
        self.assertIn('--top-k', result.stdout)


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