#!/usr/bin/env python3
"""
Test Phase 3: Core Face Processing with DeepFace

This test verifies that:
1. DeepFace can be imported and used
2. Face detection works with DeepFace
3. Face encodings are 512-dimensional (ArcFace)
4. Cosine similarity calculation works
5. Location format handling works (dict vs tuple)
6. Full end-to-end processing works
"""

import os
import sys
import tempfile
import numpy as np
from pathlib import Path

# Suppress TensorFlow warnings
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
import warnings
warnings.filterwarnings('ignore')

# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent))


def test_deepface_import():
    """Test that DeepFace can be imported"""
    print("\n🧪 Test 1: DeepFace Import")
    
    try:
        from deepface import DeepFace
        print(f"   ✓ DeepFace imported successfully")
        print(f"   ✓ Version: {DeepFace.__version__ if hasattr(DeepFace, '__version__') else 'unknown'}")
        return True
    except ImportError as e:
        print(f"   ❌ Failed to import DeepFace: {e}")
        return False


def test_deepface_detection():
    """Test DeepFace face detection"""
    print("\n🧪 Test 2: DeepFace Face Detection")
    
    try:
        from deepface import DeepFace
        
        # Check for test images
        test_folder = Path("demo_photos/testdeepface")
        if not test_folder.exists():
            test_folder = Path("demo_photos")
        
        test_images = list(test_folder.glob("*.jpg")) + list(test_folder.glob("*.JPG"))
        if not test_images:
            print("   ⚠️  No test images found, skipping")
            return True
        
        test_image = str(test_images[0])
        print(f"   Testing with: {Path(test_image).name}")
        
        # Try to detect faces
        results = DeepFace.represent(
            img_path=test_image,
            model_name='ArcFace',
            detector_backend='retinaface',
            enforce_detection=False,
            align=True
        )
        
        if results:
            print(f"   ✓ Found {len(results)} face(s)")
            
            # Check encoding dimensions
            encoding = np.array(results[0]['embedding'])
            print(f"   ✓ Encoding shape: {encoding.shape}")
            
            if len(encoding) == 512:
                print(f"   ✓ Correct encoding size (512-dimensional for ArcFace)")
            else:
                print(f"   ⚠️  Unexpected encoding size: {len(encoding)}")
            
            # Check facial_area format
            facial_area = results[0].get('facial_area', {})
            print(f"   ✓ Facial area: {facial_area}")
            
            if all(k in facial_area for k in ['x', 'y', 'w', 'h']):
                print(f"   ✓ Correct facial area format (x, y, w, h)")
            else:
                print(f"   ⚠️  Unexpected facial area format")
            
            return True
        else:
            print(f"   ⚠️  No faces detected (image may have no faces)")
            return True  # Not a failure, just no faces
            
    except Exception as e:
        print(f"   ❌ Error: {e}")
        import traceback
        traceback.print_exc()
        return False


def test_cosine_similarity():
    """Test cosine similarity calculation"""
    print("\n🧪 Test 3: Cosine Similarity Calculation")
    
    try:
        from src.core.database import DatabaseManager
        from src.core.face_processing import FaceProcessor
        
        # Create temporary database
        with tempfile.NamedTemporaryFile(delete=False, suffix='.db') as tmp:
            tmp_db_path = tmp.name
        
        db = DatabaseManager(tmp_db_path, verbose=0)
        processor = FaceProcessor(db, verbose=0)
        
        # Test with identical encodings
        enc1 = np.random.rand(512)
        distance_identical = processor._calculate_cosine_similarity(enc1, enc1)
        print(f"   ✓ Identical encodings distance: {distance_identical:.6f}")
        
        if distance_identical < 0.01:  # Should be very close to 0
            print(f"   ✓ Identical encodings produce near-zero distance")
        else:
            print(f"   ⚠️  Identical encodings distance higher than expected")
        
        # Test with different encodings
        enc2 = np.random.rand(512)
        distance_different = processor._calculate_cosine_similarity(enc1, enc2)
        print(f"   ✓ Different encodings distance: {distance_different:.6f}")
        
        if 0 < distance_different < 2:  # Should be in valid range
            print(f"   ✓ Different encodings produce valid distance")
        else:
            print(f"   ⚠️  Distance out of expected range [0, 2]")
        
        # Test with length mismatch
        enc3 = np.random.rand(128)  # Different length
        distance_mismatch = processor._calculate_cosine_similarity(enc1, enc3)
        print(f"   ✓ Mismatched length distance: {distance_mismatch:.6f}")
        
        if distance_mismatch == 2.0:  # Should return max distance
            print(f"   ✓ Mismatched lengths handled correctly")
        else:
            print(f"   ⚠️  Mismatch handling unexpected")
        
        # Cleanup
        if os.path.exists(tmp_db_path):
            os.unlink(tmp_db_path)
        
        print("   ✅ Cosine similarity calculation works correctly")
        return True
        
    except Exception as e:
        print(f"   ❌ Error: {e}")
        import traceback
        traceback.print_exc()
        return False


def test_location_format_handling():
    """Test handling of both dict and tuple location formats"""
    print("\n🧪 Test 4: Location Format Handling")
    
    try:
        # Test dict format (DeepFace)
        location_dict = {'x': 100, 'y': 150, 'w': 200, 'h': 200}
        location_str_dict = str(location_dict)
        
        import ast
        parsed_dict = ast.literal_eval(location_str_dict)
        
        if isinstance(parsed_dict, dict):
            left = parsed_dict.get('x', 0)
            top = parsed_dict.get('y', 0)
            width = parsed_dict.get('w', 0)
            height = parsed_dict.get('h', 0)
            right = left + width
            bottom = top + height
            
            print(f"   ✓ Dict format parsed: {location_dict}")
            print(f"   ✓ Converted to box: top={top}, right={right}, bottom={bottom}, left={left}")
            
            if (left == 100 and top == 150 and right == 300 and bottom == 350):
                print(f"   ✓ Dict conversion correct")
            else:
                print(f"   ❌ Dict conversion incorrect")
                return False
        
        # Legacy tuple format tests removed - only DeepFace format supported
        
        print("   ✅ DeepFace location format handled correctly")
        return True
        
    except Exception as e:
        print(f"   ❌ Error: {e}")
        import traceback
        traceback.print_exc()
        return False


def test_end_to_end_processing():
    """Test end-to-end face processing with DeepFace"""
    print("\n🧪 Test 5: End-to-End Processing")
    
    try:
        from src.core.database import DatabaseManager
        from src.core.face_processing import FaceProcessor
        
        # Check for test images
        test_folder = Path("demo_photos/testdeepface")
        if not test_folder.exists():
            test_folder = Path("demo_photos")
        
        test_images = list(test_folder.glob("*.jpg")) + list(test_folder.glob("*.JPG"))
        if not test_images:
            print("   ⚠️  No test images found, skipping")
            return True
        
        # Create temporary database
        with tempfile.NamedTemporaryFile(delete=False, suffix='.db') as tmp:
            tmp_db_path = tmp.name
        
        db = DatabaseManager(tmp_db_path, verbose=0)
        processor = FaceProcessor(db, verbose=1, 
                                 detector_backend='retinaface',
                                 model_name='ArcFace')
        
        # Add a test photo
        test_image = str(test_images[0])
        photo_id = db.add_photo(test_image, Path(test_image).name, None)
        
        if not photo_id:
            print(f"   ❌ Failed to add photo")
            return False
        
        print(f"   ✓ Added test photo (ID: {photo_id})")
        
        # Process faces
        print(f"   Processing faces...")
        count = processor.process_faces(limit=1)
        
        print(f"   ✓ Processed {count} photo(s)")
        
        # Verify results
        stats = db.get_statistics()
        print(f"   ✓ Statistics: {stats['total_faces']} faces found")
        
        if stats['total_faces'] > 0:
            # Check encoding size
            faces = db.get_all_face_encodings()
            if faces:
                face_id, encoding_bytes, person_id, quality = faces[0]
                encoding = np.frombuffer(encoding_bytes, dtype=np.float64)
                print(f"   ✓ Encoding size: {len(encoding)} dimensions")
                
                if len(encoding) == 512:
                    print(f"   ✅ Correct encoding size (512-dim ArcFace)")
                else:
                    print(f"   ⚠️  Unexpected encoding size: {len(encoding)}")
        
        # Cleanup
        if os.path.exists(tmp_db_path):
            os.unlink(tmp_db_path)
        
        print("   ✅ End-to-end processing successful")
        return True
        
    except Exception as e:
        print(f"   ❌ Error: {e}")
        import traceback
        traceback.print_exc()
        return False


def run_all_tests():
    """Run all Phase 3 tests"""
    print("=" * 70)
    print("Phase 3 DeepFace Integration Tests")
    print("=" * 70)
    
    tests = [
        ("DeepFace Import", test_deepface_import),
        ("DeepFace Detection", test_deepface_detection),
        ("Cosine Similarity", test_cosine_similarity),
        ("Location Format Handling", test_location_format_handling),
        ("End-to-End Processing", test_end_to_end_processing)
    ]
    
    results = []
    for test_name, test_func in tests:
        try:
            result = test_func()
            results.append((test_name, result))
        except Exception as e:
            print(f"   ❌ Test failed with exception: {e}")
            import traceback
            traceback.print_exc()
            results.append((test_name, False))
    
    print("\n" + "=" * 70)
    print("Test Results Summary")
    print("=" * 70)
    
    for test_name, result in results:
        status = "✅ PASS" if result else "❌ FAIL"
        print(f"  {status}: {test_name}")
    
    passed = sum(1 for _, result in results if result)
    total = len(results)
    
    print()
    print(f"Tests passed: {passed}/{total}")
    print("=" * 70)
    
    return all(result for _, result in results)


if __name__ == "__main__":
    success = run_all_tests()
    sys.exit(0 if success else 1)


