#!/usr/bin/env python3
"""
Retroactive Face Recognition Scanner

Scans existing files in a directory and moves unmatched files to review queue
while storing their original destination paths for later restoration.
"""

import os
import sys
import json
import shutil
from pathlib import Path

# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Bootstrap PostgreSQL adapter before any database imports
from modules.db_bootstrap import bootstrap_database
bootstrap_database()

from modules.face_recognition_module import FaceRecognitionModule
from modules.unified_database import UnifiedDatabase
from modules.settings_manager import SettingsManager

# Configuration
SCAN_BASE_DIR = "/opt/immich/md"
REVIEW_DIR = "/opt/immich/review"
DATABASE_PATH = "/opt/media-downloader/database/media_downloader.db"

# Supported file extensions
IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.heic'}
VIDEO_EXTENSIONS = {'.mp4', '.mov', '.avi', '.mkv', '.webm', '.flv', '.m4v'}
SUPPORTED_EXTENSIONS = IMAGE_EXTENSIONS | VIDEO_EXTENSIONS


class RetroactiveFaceScanner:
    def __init__(self, scan_only=False):
        self.db = UnifiedDatabase()
        self.settings_manager = SettingsManager(DATABASE_PATH)
        self.face_module = FaceRecognitionModule(unified_db=self.db)
        self.scan_only = scan_only
        self.stats = {
            'total_files': 0,
            'matched': 0,
            'unmatched': 0,
            'errors': 0,
            'skipped': 0
        }


    def get_relative_path(self, full_path):
        """Get path relative to SCAN_BASE_DIR"""
        try:
            return os.path.relpath(full_path, SCAN_BASE_DIR)
        except ValueError:
            return full_path

    def scan_directory(self, directory):
        """Recursively scan directory for media files"""
        print(f"\n🔍 Scanning directory: {directory}")
        print("=" * 70)

        for root, dirs, files in os.walk(directory):
            for filename in files:
                file_path = os.path.join(root, filename)
                file_ext = os.path.splitext(filename)[1].lower()

                if file_ext not in SUPPORTED_EXTENSIONS:
                    continue

                self.stats['total_files'] += 1
                self.process_file(file_path, file_ext in VIDEO_EXTENSIONS)

        self.print_stats()

    def process_file(self, file_path, is_video):
        """Process a single file with face recognition"""
        filename = os.path.basename(file_path)
        relative_path = self.get_relative_path(os.path.dirname(file_path))

        print(f"\n[{self.stats['total_files']}] {filename}")
        print(f"    Location: {relative_path}")

        try:
            # Get face recognition settings
            settings = self.settings_manager.get('face_recognition', {})
            if not settings.get('enabled', False):
                print("    ⚠ Face recognition is disabled in settings")
                self.stats['skipped'] += 1
                return

            tolerance = settings.get('tolerance', 0.6)

            # Check for faces
            print(f"    🔍 Checking for faces (tolerance: {tolerance})...")
            result = self.face_module.check_image(file_path, tolerance=tolerance, is_video=is_video)

            # Log scan result to database
            try:
                self.db.log_face_recognition_scan(
                    file_path=file_path,
                    has_match=result.get('has_match', False),
                    matched_person=result.get('person_name'),
                    confidence=result.get('confidence'),
                    face_count=result.get('face_count', 0),
                    scan_type='retroactive'
                )
            except Exception as db_err:
                print(f"    ⚠ Warning: Failed to log to database: {db_err}")

            if result.get('has_match'):
                person_name = result.get('person_name', 'Unknown')
                confidence = result.get('confidence', 0)
                print(f"    ✓ MATCH: {person_name} (confidence: {confidence:.2%})")
                self.stats['matched'] += 1
            else:
                if self.scan_only:
                    print(f"    ✗ NO MATCH (scan-only mode, not moving file)")
                    self.stats['unmatched'] += 1
                else:
                    print(f"    ✗ NO MATCH - Moving to review queue...")
                    self.move_to_review(file_path, file_path)  # Pass full path as original path
                    self.stats['unmatched'] += 1

        except Exception as e:
            print(f"    ✗ ERROR: {str(e)}")
            self.stats['errors'] += 1

    def move_to_review(self, file_path, original_path):
        """Move file to review queue and update database with intended_path"""
        try:
            from pathlib import Path
            
            # Maintain directory structure in review queue
            base_path = Path(SCAN_BASE_DIR)
            file_path_obj = Path(file_path)
            
            if file_path_obj.is_relative_to(base_path):
                # Get relative path from base
                relative_path = file_path_obj.relative_to(base_path)
                # Recreate under review directory
                review_path = Path(REVIEW_DIR) / relative_path
            else:
                # Fallback to flat structure if not under base path
                review_path = Path(REVIEW_DIR) / file_path_obj.name
            
            # Ensure parent directory exists
            review_path.parent.mkdir(parents=True, exist_ok=True)

            # Move file
            shutil.move(file_path, str(review_path))

            # Update database entry with new review path and store intended_path in metadata
            with self.db.get_connection(for_write=True) as conn:
                cursor = conn.cursor()

                # Find the download entry for this file
                cursor.execute('SELECT id, metadata FROM downloads WHERE file_path = ?', (file_path,))
                row = cursor.fetchone()

                if row:
                    download_id = row['id']
                    existing_metadata = json.loads(row['metadata']) if row['metadata'] else {}

                    # Add intended_path to metadata
                    existing_metadata['intended_path'] = file_path

                    # Update the download record with new review path and metadata
                    cursor.execute('''
                        UPDATE downloads
                        SET file_path = ?, metadata = ?
                        WHERE id = ?
                    ''', (str(review_path), json.dumps(existing_metadata), download_id))

                    print(f"    → Moved to: {review_path}")
                    print(f"    → Original path stored in database: {file_path}")
                else:
                    print(f"    ⚠ Warning: No database entry found for {file_path}")
                    print(f"    → Moved to: {review_path} (not tracked in database)")

        except Exception as e:
            print(f"    ✗ Failed to move file: {e}")
            raise

    def print_stats(self):
        """Print final statistics"""
        print("\n" + "=" * 70)
        print("📊 SCAN COMPLETE")
        print("=" * 70)
        print(f"Total files scanned:  {self.stats['total_files']}")
        print(f"✓ Matched:            {self.stats['matched']}")
        print(f"✗ Unmatched (moved):  {self.stats['unmatched']}")
        print(f"⚠ Errors:             {self.stats['errors']}")
        print(f"⊘ Skipped:            {self.stats['skipped']}")
        print("=" * 70)


def main():
    import argparse

    parser = argparse.ArgumentParser(
        description='Retroactively scan existing files with face recognition',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Scan social media directory
  python3 scripts/retroactive_face_scan.py "social media"

  # Scan specific subdirectory
  python3 scripts/retroactive_face_scan.py "social media/instagram"

  # Scan with full path
  python3 scripts/retroactive_face_scan.py "/opt/immich/md/social media"

Note: Original paths are stored in the database metadata as 'intended_path'.
      Use the Review UI to keep/delete/add reference to moved files.
        """
    )

    parser.add_argument(
        'directory',
        help='Directory to scan (relative to /opt/immich/md or absolute path)'
    )

    parser.add_argument(
        '--dry-run',
        action='store_true',
        help='Show what would be done without moving files'
    )

    parser.add_argument(
        '--scan-only',
        action='store_true',
        help='Scan and update database only - do not move unmatched files to review'
    )

    args = parser.parse_args()

    # Determine scan directory
    if os.path.isabs(args.directory):
        scan_dir = args.directory
    else:
        scan_dir = os.path.join(SCAN_BASE_DIR, args.directory)

    if not os.path.exists(scan_dir):
        print(f"✗ Error: Directory does not exist: {scan_dir}")
        sys.exit(1)

    if not os.path.isdir(scan_dir):
        print(f"✗ Error: Not a directory: {scan_dir}")
        sys.exit(1)

    if args.dry_run:
        print("🔍 DRY RUN MODE - No files will be moved")
        print("=" * 70)

    print(f"\n🎯 Retroactive Face Recognition Scan")
    print(f"Scan directory: {scan_dir}")
    print(f"Review queue:   {REVIEW_DIR}")

    # Confirm
    response = input("\nContinue? (y/n): ")
    if response.lower() != 'y':
        print("Cancelled.")
        sys.exit(0)

    # Run scan
    scanner = RetroactiveFaceScanner(scan_only=args.scan_only)

    if args.scan_only:
        print("🔍 SCAN-ONLY MODE - Files will NOT be moved to review")
        print("=" * 70)

    if args.dry_run:
        # TODO: Implement dry run mode
        print("\n⚠ Dry run mode not yet implemented")
        sys.exit(1)
    else:
        scanner.scan_directory(scan_dir)

    print(f"\n✓ Scan complete!")
    if args.scan_only:
        print(f"\n📝 Scan-only mode: Database updated with face recognition results.")
        print(f"No files were moved. Use the GUI to filter by 'Not Scanned' or 'No Match'.")
    else:
        print(f"\nUnmatched files have been moved to: {REVIEW_DIR}")
        print(f"Use the Review UI at http://your-server:5173/review to process them.")
        print(f"\nOriginal paths stored in database metadata.")


if __name__ == '__main__':
    main()
