"""
File System Backup Script for Django System
Task 2.2: Create file system backup

This script creates a comprehensive backup of the Django project directory,
excluding virtual environments and cache directories.
"""

import os
import shutil
from datetime import datetime
from pathlib import Path
import json

# Configuration
SOURCE_DIR = r"c:\Users\Teamjoint company\Desktop\branchsystem"
BACKUP_BASE_DIR = os.path.join(SOURCE_DIR, "backups")

# Directories and patterns to exclude
EXCLUDE_DIRS = {
    'venv',
    '__pycache__',
    '.hypothesis',
    'node_modules',
    '.git',
    'backups',  # Don't backup the backups directory itself
}

EXCLUDE_EXTENSIONS = {
    '.pyc',
    '.pyo',
    '.pyd',
}


def create_timestamp():
    """Create timestamp in YYYYMMDD_HHMMSS format"""
    return datetime.now().strftime("%Y%m%d_%H%M%S")


def should_exclude(path, base_path):
    """
    Determine if a path should be excluded from backup
    
    Args:
        path: Path to check
        base_path: Base directory path
        
    Returns:
        bool: True if path should be excluded
    """
    rel_path = os.path.relpath(path, base_path)
    path_parts = Path(rel_path).parts
    
    # Check if any part of the path matches excluded directories
    for part in path_parts:
        if part in EXCLUDE_DIRS:
            return True
    
    # Check file extension
    if os.path.isfile(path):
        _, ext = os.path.splitext(path)
        if ext in EXCLUDE_EXTENSIONS:
            return True
    
    return False


def create_backup():
    """
    Create a complete backup of the Django project
    
    Returns:
        dict: Backup results including path, file count, and manifest
    """
    # Create timestamp
    timestamp = create_timestamp()
    backup_dir_name = f"django_system_backup_{timestamp}"
    backup_dir = os.path.join(BACKUP_BASE_DIR, backup_dir_name)
    
    print(f"Creating backup: {backup_dir_name}")
    print(f"Source: {SOURCE_DIR}")
    print(f"Destination: {backup_dir}")
    print()
    
    # Create backup directory
    os.makedirs(backup_dir, exist_ok=True)
    
    # Track backed up files
    backed_up_files = []
    file_count = 0
    total_size = 0
    
    # Walk through source directory
    for root, dirs, files in os.walk(SOURCE_DIR):
        # Filter out excluded directories
        dirs[:] = [d for d in dirs if not should_exclude(os.path.join(root, d), SOURCE_DIR)]
        
        for file in files:
            source_path = os.path.join(root, file)
            
            # Skip excluded files
            if should_exclude(source_path, SOURCE_DIR):
                continue
            
            # Calculate relative path
            rel_path = os.path.relpath(source_path, SOURCE_DIR)
            dest_path = os.path.join(backup_dir, rel_path)
            
            # Create destination directory if needed
            dest_dir = os.path.dirname(dest_path)
            os.makedirs(dest_dir, exist_ok=True)
            
            # Copy file
            try:
                shutil.copy2(source_path, dest_path)
                file_size = os.path.getsize(source_path)
                
                backed_up_files.append({
                    'path': rel_path,
                    'size': file_size,
                    'modified': datetime.fromtimestamp(os.path.getmtime(source_path)).isoformat()
                })
                
                file_count += 1
                total_size += file_size
                
                if file_count % 100 == 0:
                    print(f"Backed up {file_count} files...")
                    
            except Exception as e:
                print(f"Error copying {rel_path}: {str(e)}")
    
    print(f"\nBackup complete!")
    print(f"Total files backed up: {file_count}")
    print(f"Total size: {total_size / (1024*1024):.2f} MB")
    
    # Create manifest file
    manifest_path = os.path.join(backup_dir, "manifest.txt")
    create_manifest(backed_up_files, manifest_path, timestamp, file_count, total_size)
    
    # Create JSON manifest for programmatic access
    json_manifest_path = os.path.join(backup_dir, "manifest.json")
    create_json_manifest(backed_up_files, json_manifest_path, timestamp, file_count, total_size)
    
    return {
        'backup_dir': backup_dir,
        'timestamp': timestamp,
        'file_count': file_count,
        'total_size': total_size,
        'manifest_path': manifest_path,
        'json_manifest_path': json_manifest_path
    }


def create_manifest(files, manifest_path, timestamp, file_count, total_size):
    """
    Create a text manifest file listing all backed up files
    
    Args:
        files: List of file dictionaries
        manifest_path: Path to manifest file
        timestamp: Backup timestamp
        file_count: Total number of files
        total_size: Total size in bytes
    """
    with open(manifest_path, 'w', encoding='utf-8') as f:
        f.write("=" * 80 + "\n")
        f.write("Django System Backup Manifest\n")
        f.write("=" * 80 + "\n\n")
        
        f.write(f"Backup Timestamp: {timestamp}\n")
        f.write(f"Backup Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"Source Directory: {SOURCE_DIR}\n")
        f.write(f"Total Files: {file_count}\n")
        f.write(f"Total Size: {total_size / (1024*1024):.2f} MB\n\n")
        
        f.write("Excluded Directories:\n")
        for exclude_dir in sorted(EXCLUDE_DIRS):
            f.write(f"  - {exclude_dir}\n")
        f.write("\n")
        
        f.write("Excluded Extensions:\n")
        for ext in sorted(EXCLUDE_EXTENSIONS):
            f.write(f"  - {ext}\n")
        f.write("\n")
        
        f.write("=" * 80 + "\n")
        f.write("Backed Up Files\n")
        f.write("=" * 80 + "\n\n")
        
        # Group files by extension
        files_by_ext = {}
        for file_info in files:
            _, ext = os.path.splitext(file_info['path'])
            ext = ext if ext else '(no extension)'
            if ext not in files_by_ext:
                files_by_ext[ext] = []
            files_by_ext[ext].append(file_info)
        
        # Write summary by extension
        f.write("File Count by Extension:\n")
        f.write("-" * 80 + "\n")
        for ext in sorted(files_by_ext.keys()):
            count = len(files_by_ext[ext])
            size = sum(f['size'] for f in files_by_ext[ext])
            f.write(f"{ext:20s} {count:6d} files  {size/(1024*1024):10.2f} MB\n")
        f.write("\n")
        
        # Write detailed file list
        f.write("Detailed File List:\n")
        f.write("-" * 80 + "\n")
        f.write(f"{'File Path':<60s} {'Size (KB)':>10s} {'Modified':>20s}\n")
        f.write("-" * 80 + "\n")
        
        for file_info in sorted(files, key=lambda x: x['path']):
            path = file_info['path']
            size_kb = file_info['size'] / 1024
            modified = file_info['modified'].split('T')[0]  # Just the date
            
            # Truncate long paths
            if len(path) > 60:
                path = "..." + path[-57:]
            
            f.write(f"{path:<60s} {size_kb:>10.2f} {modified:>20s}\n")
    
    print(f"\nManifest created: {manifest_path}")


def create_json_manifest(files, json_path, timestamp, file_count, total_size):
    """
    Create a JSON manifest file for programmatic access
    
    Args:
        files: List of file dictionaries
        json_path: Path to JSON manifest file
        timestamp: Backup timestamp
        file_count: Total number of files
        total_size: Total size in bytes
    """
    manifest_data = {
        'backup_info': {
            'timestamp': timestamp,
            'date': datetime.now().isoformat(),
            'source_directory': SOURCE_DIR,
            'total_files': file_count,
            'total_size_bytes': total_size,
            'total_size_mb': round(total_size / (1024*1024), 2)
        },
        'exclusions': {
            'directories': sorted(list(EXCLUDE_DIRS)),
            'extensions': sorted(list(EXCLUDE_EXTENSIONS))
        },
        'files': files
    }
    
    with open(json_path, 'w', encoding='utf-8') as f:
        json.dump(manifest_data, f, indent=2)
    
    print(f"JSON manifest created: {json_path}")


def verify_backup(backup_dir, file_count):
    """
    Verify the backup was created successfully
    
    Args:
        backup_dir: Path to backup directory
        file_count: Expected number of files
        
    Returns:
        bool: True if verification passed
    """
    print("\nVerifying backup...")
    
    # Check if backup directory exists
    if not os.path.exists(backup_dir):
        print(f"ERROR: Backup directory not found: {backup_dir}")
        return False
    
    # Count files in backup
    actual_count = 0
    for root, dirs, files in os.walk(backup_dir):
        # Don't count manifest files
        actual_count += len([f for f in files if not f.startswith('manifest')])
    
    if actual_count != file_count:
        print(f"WARNING: File count mismatch. Expected {file_count}, found {actual_count}")
        return False
    
    # Check for critical files
    critical_files = [
        'manage.py',
        'requirements.txt',
        os.path.join('branch_system', 'settings.py'),
    ]
    
    for critical_file in critical_files:
        full_path = os.path.join(backup_dir, critical_file)
        if not os.path.exists(full_path):
            print(f"WARNING: Critical file missing: {critical_file}")
            return False
    
    print("Backup verification PASSED")
    return True


def main():
    """Main execution function"""
    print("=" * 80)
    print("Django System Backup Script")
    print("Task 2.2: Create file system backup")
    print("=" * 80)
    print()
    
    # Create backup
    result = create_backup()
    
    # Verify backup
    verification_passed = verify_backup(result['backup_dir'], result['file_count'])
    
    # Print summary
    print("\n" + "=" * 80)
    print("Backup Summary")
    print("=" * 80)
    print(f"Backup Directory: {result['backup_dir']}")
    print(f"Timestamp: {result['timestamp']}")
    print(f"Files Backed Up: {result['file_count']}")
    print(f"Total Size: {result['total_size'] / (1024*1024):.2f} MB")
    print(f"Manifest File: {result['manifest_path']}")
    print(f"JSON Manifest: {result['json_manifest_path']}")
    print(f"Verification: {'PASSED' if verification_passed else 'FAILED'}")
    print("=" * 80)
    
    return result


if __name__ == "__main__":
    main()
