#!/usr/bin/env python3
"""
Phase 4 Integration Test: GUI Updates for DeepFace
Tests that all GUI panels correctly handle DeepFace metadata and location formats
"""

import os
import sys
import tempfile
import sqlite3
from pathlib import Path

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

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

from src.core.database import DatabaseManager
from src.core.face_processing import FaceProcessor
from src.core.config import DEEPFACE_DETECTOR_BACKEND, DEEPFACE_MODEL_NAME


def test_database_schema():
    """Test 1: Verify database schema has DeepFace columns"""
    print("\n" + "="*60)
    print("Test 1: Database Schema with DeepFace Columns")
    print("="*60)
    
    try:
        # Create in-memory database
        db = DatabaseManager(":memory:", verbose=0)
        
        # Check faces table schema
        with db.get_db_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("PRAGMA table_info(faces)")
            columns = {row[1]: row[2] for row in cursor.fetchall()}
        
        # Verify DeepFace columns exist
        required_columns = {
            'id': 'INTEGER',
            'photo_id': 'INTEGER',
            'person_id': 'INTEGER',
            'encoding': 'BLOB',
            'location': 'TEXT',
            'confidence': 'REAL',
            'quality_score': 'REAL',
            'detector_backend': 'TEXT',
            'model_name': 'TEXT',
            'face_confidence': 'REAL'
        }
        
        missing_columns = []
        for col_name, col_type in required_columns.items():
            if col_name not in columns:
                missing_columns.append(col_name)
            else:
                print(f"✓ Column '{col_name}' exists with type '{columns[col_name]}'")
        
        if missing_columns:
            print(f"\n❌ FAIL: Missing columns: {missing_columns}")
            return False
        
        print("\n✅ PASS: All DeepFace columns present in database schema")
        return True
        
    except Exception as e:
        print(f"\n❌ FAIL: {e}")
        import traceback
        traceback.print_exc()
        return False


def test_face_data_retrieval():
    """Test 2: Verify face data retrieval includes DeepFace metadata"""
    print("\n" + "="*60)
    print("Test 2: Face Data Retrieval with DeepFace Metadata")
    print("="*60)
    
    try:
        # Create in-memory database
        db = DatabaseManager(":memory:", verbose=0)
        
        # Create a test photo
        test_photo_path = "/tmp/test_photo.jpg"
        photo_id = db.add_photo(test_photo_path, "test_photo.jpg", None)
        
        # Create a test face with DeepFace metadata
        import numpy as np
        test_encoding = np.random.rand(512).astype(np.float64)  # 512-dim for ArcFace
        test_location = "{'x': 100, 'y': 100, 'w': 50, 'h': 50}"
        
        face_id = db.add_face(
            photo_id=photo_id,
            encoding=test_encoding.tobytes(),
            location=test_location,
            confidence=0.0,
            quality_score=0.85,
            person_id=None,
            detector_backend='retinaface',
            model_name='ArcFace',
            face_confidence=0.95
        )
        
        print(f"✓ Created test face with ID {face_id}")
        
        # Query the face data (simulating GUI panel queries)
        with db.get_db_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT f.id, f.photo_id, p.path, p.filename, f.location,
                       f.face_confidence, f.quality_score, f.detector_backend, f.model_name
                FROM faces f
                JOIN photos p ON f.photo_id = p.id
                WHERE f.id = ?
            """, (face_id,))
            
            result = cursor.fetchone()
        
        if not result:
            print("\n❌ FAIL: Could not retrieve face data")
            return False
        
        # Unpack the result (9 fields)
        face_id_ret, photo_id_ret, path, filename, location, face_conf, quality, detector, model = result
        
        print(f"✓ Retrieved face data:")
        print(f"  - Face ID: {face_id_ret}")
        print(f"  - Photo ID: {photo_id_ret}")
        print(f"  - Location: {location}")
        print(f"  - Face Confidence: {face_conf}")
        print(f"  - Quality Score: {quality}")
        print(f"  - Detector: {detector}")
        print(f"  - Model: {model}")
        
        # Verify the metadata
        if face_conf != 0.95:
            print(f"\n❌ FAIL: Face confidence mismatch: expected 0.95, got {face_conf}")
            return False
        
        if quality != 0.85:
            print(f"\n❌ FAIL: Quality score mismatch: expected 0.85, got {quality}")
            return False
        
        if detector != 'retinaface':
            print(f"\n❌ FAIL: Detector mismatch: expected 'retinaface', got {detector}")
            return False
        
        if model != 'ArcFace':
            print(f"\n❌ FAIL: Model mismatch: expected 'ArcFace', got {model}")
            return False
        
        print("\n✅ PASS: Face data retrieval includes all DeepFace metadata")
        return True
        
    except Exception as e:
        print(f"\n❌ FAIL: {e}")
        import traceback
        traceback.print_exc()
        return False


def test_location_format_handling():
    """Test 3: Verify both location formats are handled correctly"""
    print("\n" + "="*60)
    print("Test 3: Location Format Handling (Dict & Tuple)")
    print("="*60)
    
    try:
        # Test both location formats
        deepface_location = "{'x': 100, 'y': 150, 'w': 80, 'h': 90}"
        legacy_location = "(150, 180, 240, 100)"
        
        # Parse DeepFace dict format
        import ast
        deepface_loc = ast.literal_eval(deepface_location)
        
        if not isinstance(deepface_loc, dict):
            print(f"❌ FAIL: DeepFace location not parsed as dict")
            return False
        
        if 'x' not in deepface_loc or 'y' not in deepface_loc or 'w' not in deepface_loc or 'h' not in deepface_loc:
            print(f"❌ FAIL: DeepFace location missing required keys")
            return False
        
        print(f"✓ DeepFace format parsed correctly: {deepface_loc}")
        
        # Legacy tuple format tests removed - only DeepFace format supported
        print(f"✓ DeepFace format is the only supported format")
        
        print("\n✅ PASS: DeepFace location format handled correctly")
        return True
        
    except Exception as e:
        print(f"\n❌ FAIL: {e}")
        import traceback
        traceback.print_exc()
        return False


def test_face_processor_configuration():
    """Test 4: Verify FaceProcessor accepts DeepFace configuration"""
    print("\n" + "="*60)
    print("Test 4: FaceProcessor DeepFace Configuration")
    print("="*60)
    
    try:
        # Create in-memory database
        db = DatabaseManager(":memory:", verbose=0)
        
        # Create FaceProcessor with default config
        processor_default = FaceProcessor(db, verbose=0)
        
        print(f"✓ Default detector: {processor_default.detector_backend}")
        print(f"✓ Default model: {processor_default.model_name}")
        
        if processor_default.detector_backend != DEEPFACE_DETECTOR_BACKEND:
            print(f"❌ FAIL: Default detector mismatch")
            return False
        
        if processor_default.model_name != DEEPFACE_MODEL_NAME:
            print(f"❌ FAIL: Default model mismatch")
            return False
        
        # Create FaceProcessor with custom config
        processor_custom = FaceProcessor(db, verbose=0, 
                                        detector_backend='mtcnn',
                                        model_name='Facenet512')
        
        print(f"✓ Custom detector: {processor_custom.detector_backend}")
        print(f"✓ Custom model: {processor_custom.model_name}")
        
        if processor_custom.detector_backend != 'mtcnn':
            print(f"❌ FAIL: Custom detector not applied")
            return False
        
        if processor_custom.model_name != 'Facenet512':
            print(f"❌ FAIL: Custom model not applied")
            return False
        
        print("\n✅ PASS: FaceProcessor correctly configured with DeepFace settings")
        return True
        
    except Exception as e:
        print(f"\n❌ FAIL: {e}")
        import traceback
        traceback.print_exc()
        return False


def test_gui_panel_compatibility():
    """Test 5: Verify GUI panels can unpack face data correctly"""
    print("\n" + "="*60)
    print("Test 5: GUI Panel Data Unpacking")
    print("="*60)
    
    try:
        # Create in-memory database
        db = DatabaseManager(":memory:", verbose=0)
        
        # Create test photo and face
        test_photo_path = "/tmp/test_photo.jpg"
        photo_id = db.add_photo(test_photo_path, "test_photo.jpg", None)
        
        import numpy as np
        test_encoding = np.random.rand(512).astype(np.float64)
        test_location = "{'x': 100, 'y': 100, 'w': 50, 'h': 50}"
        
        face_id = db.add_face(
            photo_id=photo_id,
            encoding=test_encoding.tobytes(),
            location=test_location,
            confidence=0.0,
            quality_score=0.85,
            person_id=None,
            detector_backend='retinaface',
            model_name='ArcFace',
            face_confidence=0.95
        )
        
        # Simulate identify_panel query
        with db.get_db_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT f.id, f.photo_id, p.path, p.filename, f.location,
                       f.face_confidence, f.quality_score, f.detector_backend, f.model_name
                FROM faces f
                JOIN photos p ON f.photo_id = p.id
                WHERE f.person_id IS NULL
            """)
            
            faces = cursor.fetchall()
        
        if not faces:
            print("❌ FAIL: No faces retrieved")
            return False
        
        # Simulate unpacking in identify_panel
        for face_tuple in faces:
            face_id, photo_id, photo_path, filename, location, face_conf, quality, detector, model = face_tuple
            
            print(f"✓ Unpacked identify_panel data:")
            print(f"  - Face ID: {face_id}")
            print(f"  - Photo ID: {photo_id}")
            print(f"  - Location: {location}")
            print(f"  - Face Confidence: {face_conf}")
            print(f"  - Quality: {quality}")
            print(f"  - Detector/Model: {detector}/{model}")
        
        # Simulate auto_match_panel query
        with db.get_db_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT f.id, f.person_id, f.photo_id, f.location, p.filename, f.quality_score,
                       f.face_confidence, f.detector_backend, f.model_name
                FROM faces f
                JOIN photos p ON f.photo_id = p.id
            """)
            
            faces = cursor.fetchall()
        
        # Simulate unpacking in auto_match_panel (uses tuple indexing)
        for face in faces:
            face_id = face[0]
            person_id = face[1]
            photo_id = face[2]
            location = face[3]
            filename = face[4]
            quality = face[5]
            face_conf = face[6]
            detector = face[7]
            model = face[8]
            
            print(f"✓ Unpacked auto_match_panel data (tuple indexing):")
            print(f"  - Face ID: {face_id}")
            print(f"  - Quality: {quality}")
            print(f"  - Face Confidence: {face_conf}")
        
        # Simulate modify_panel query
        with db.get_db_connection() as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT f.id, f.photo_id, p.path, p.filename, f.location,
                       f.face_confidence, f.quality_score, f.detector_backend, f.model_name
                FROM faces f
                JOIN photos p ON f.photo_id = p.id
            """)
            
            faces = cursor.fetchall()
        
        # Simulate unpacking in modify_panel
        for face_tuple in faces:
            face_id, photo_id, photo_path, filename, location, face_conf, quality, detector, model = face_tuple
            
            print(f"✓ Unpacked modify_panel data:")
            print(f"  - Face ID: {face_id}")
            print(f"  - Quality: {quality}")
        
        print("\n✅ PASS: All GUI panels can correctly unpack face data")
        return True
        
    except Exception as e:
        print(f"\n❌ FAIL: {e}")
        import traceback
        traceback.print_exc()
        return False


def main():
    """Run all Phase 4 tests"""
    print("\n" + "="*70)
    print("PHASE 4 INTEGRATION TEST SUITE: GUI Updates for DeepFace")
    print("="*70)
    
    tests = [
        ("Database Schema", test_database_schema),
        ("Face Data Retrieval", test_face_data_retrieval),
        ("Location Format Handling", test_location_format_handling),
        ("FaceProcessor Configuration", test_face_processor_configuration),
        ("GUI Panel Compatibility", test_gui_panel_compatibility),
    ]
    
    results = []
    for test_name, test_func in tests:
        try:
            result = test_func()
            results.append((test_name, result))
        except Exception as e:
            print(f"\n❌ Test '{test_name}' crashed: {e}")
            import traceback
            traceback.print_exc()
            results.append((test_name, False))
    
    # Print summary
    print("\n" + "="*70)
    print("TEST SUMMARY")
    print("="*70)
    
    passed = 0
    failed = 0
    for test_name, result in results:
        status = "✅ PASS" if result else "❌ FAIL"
        print(f"{status}: {test_name}")
        if result:
            passed += 1
        else:
            failed += 1
    
    print("="*70)
    print(f"Tests passed: {passed}/{len(tests)}")
    print(f"Tests failed: {failed}/{len(tests)}")
    print("="*70)
    
    if failed == 0:
        print("\n🎉 ALL TESTS PASSED! Phase 4 GUI integration is complete!")
        return 0
    else:
        print(f"\n⚠️  {failed} test(s) failed. Please review the errors above.")
        return 1


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

