"""
Drag & Drop System für das MYP-System
====================================

Dieses Modul stellt umfassende Drag & Drop Funktionalität bereit:
- Job-Reihenfolge per Drag & Drop ändern
- Multi-File-Upload mit Drag & Drop
- Visuelles Feedback und Validierung
- Progress-Tracking für Uploads
- Barrierefreie Alternative-Eingaben
- Touch-Support für mobile Geräte
"""

import os
import json
import mimetypes
from datetime import datetime
from typing import Dict, List, Any, Optional, Tuple, Union
from dataclasses import dataclass, asdict
from flask import request, jsonify, current_app
from flask_login import current_user

from utils.logging_config import get_logger
from models import Job, Printer, JobOrder, get_db_session
from utils.file_manager import save_job_file, save_temp_file
from config.settings import ALLOWED_EXTENSIONS, MAX_FILE_SIZE, UPLOAD_FOLDER

logger = get_logger("drag_drop")

@dataclass
class DragDropConfig:
    """Konfiguration für Drag & Drop Bereiche"""
    element_id: str
    accepted_types: List[str]
    max_files: int = 10
    max_file_size: int = MAX_FILE_SIZE
    multiple: bool = True
    auto_upload: bool = False
    preview_mode: str = "thumbnail"  # thumbnail, list, grid
    validation_rules: Dict[str, Any] = None

@dataclass
class FileUploadProgress:
    """Upload-Progress für eine Datei"""
    file_id: str
    filename: str
    size: int
    uploaded_bytes: int = 0
    status: str = "pending"  # pending, uploading, completed, error
    error_message: str = None
    upload_speed: float = 0.0
    eta_seconds: int = 0

class DragDropManager:
    """Manager für Drag & Drop Operationen"""
    
    def __init__(self):
        self.upload_sessions: Dict[str, Dict[str, FileUploadProgress]] = {}
        self.job_order_cache: Dict[int, List[int]] = {}  # printer_id -> job_ids order
    
    def create_upload_session(self, session_id: str) -> str:
        """Erstellt eine neue Upload-Session"""
        self.upload_sessions[session_id] = {}
        logger.info(f"Upload-Session erstellt: {session_id}")
        return session_id
    
    def add_file_to_session(self, session_id: str, file_progress: FileUploadProgress):
        """Fügt eine Datei zur Upload-Session hinzu"""
        if session_id not in self.upload_sessions:
            self.upload_sessions[session_id] = {}
        
        self.upload_sessions[session_id][file_progress.file_id] = file_progress
    
    def update_file_progress(self, session_id: str, file_id: str, 
                           uploaded_bytes: int, status: str = None, 
                           error_message: str = None):
        """Aktualisiert Upload-Progress"""
        if session_id in self.upload_sessions and file_id in self.upload_sessions[session_id]:
            progress = self.upload_sessions[session_id][file_id]
            progress.uploaded_bytes = uploaded_bytes
            
            if status:
                progress.status = status
            if error_message:
                progress.error_message = error_message
            
            # Berechne Upload-Geschwindigkeit und ETA
            if progress.size > 0:
                progress_percent = uploaded_bytes / progress.size
                if progress_percent > 0:
                    progress.eta_seconds = int((progress.size - uploaded_bytes) / max(progress.upload_speed, 1))
    
    def get_session_progress(self, session_id: str) -> Dict[str, Any]:
        """Holt Progress-Informationen für eine Session"""
        if session_id not in self.upload_sessions:
            return {'files': [], 'total_progress': 0}
        
        files = list(self.upload_sessions[session_id].values())
        total_size = sum(f.size for f in files)
        total_uploaded = sum(f.uploaded_bytes for f in files)
        
        total_progress = (total_uploaded / total_size * 100) if total_size > 0 else 0
        
        return {
            'files': [asdict(f) for f in files],
            'total_progress': total_progress,
            'total_size': total_size,
            'total_uploaded': total_uploaded,
            'files_completed': len([f for f in files if f.status == 'completed']),
            'files_error': len([f for f in files if f.status == 'error'])
        }
    
    def cleanup_session(self, session_id: str):
        """Bereinigt eine Upload-Session"""
        if session_id in self.upload_sessions:
            del self.upload_sessions[session_id]
            logger.info(f"Upload-Session bereinigt: {session_id}")
    
    def update_job_order(self, printer_id: int, job_ids: List[int]) -> bool:
        """Aktualisiert die Job-Reihenfolge für einen Drucker"""
        try:
            # Aktuelle Benutzer-ID für Audit-Trail
            user_id = current_user.id if current_user.is_authenticated else None
            
            # Validierung der Eingaben
            if not isinstance(printer_id, int) or printer_id <= 0:
                logger.error(f"Ungültige Drucker-ID: {printer_id}")
                return False
            
            if not isinstance(job_ids, list) or not job_ids:
                logger.error(f"Ungültige Job-IDs Liste: {job_ids}")
                return False
            
            # Duplikate entfernen und Reihenfolge beibehalten
            unique_job_ids = []
            seen = set()
            for job_id in job_ids:
                if job_id not in seen:
                    unique_job_ids.append(job_id)
                    seen.add(job_id)
            
            if len(unique_job_ids) != len(job_ids):
                logger.warning(f"Duplikate in Job-IDs entfernt: {job_ids} -> {unique_job_ids}")
                job_ids = unique_job_ids
            
            # Datenbank-Implementierung mit JobOrder-Tabelle
            success = JobOrder.update_printer_order(
                printer_id=printer_id,
                job_ids=job_ids,
                modified_by_user_id=user_id
            )
            
            if success:
                # Cache aktualisieren
                self.job_order_cache[printer_id] = job_ids
                
                logger.info(f"Job-Reihenfolge für Drucker {printer_id} erfolgreich aktualisiert: {job_ids}")
                logger.info(f"Aktualisiert von Benutzer: {user_id}")
                
                # Optional: Bereinigung ungültiger Einträge im Hintergrund
                self._schedule_cleanup()
                
                return True
            else:
                logger.error(f"Fehler beim Speichern der Job-Reihenfolge in der Datenbank")
                return False
                
        except Exception as e:
            logger.error(f"Unerwarteter Fehler beim Aktualisieren der Job-Reihenfolge: {str(e)}")
            return False
    
    def get_job_order(self, printer_id: int) -> List[int]:
        """Holt die aktuelle Job-Reihenfolge für einen Drucker"""
        try:
            # Erst aus Cache versuchen
            if printer_id in self.job_order_cache:
                cached_order = self.job_order_cache[printer_id]
                logger.debug(f"Job-Reihenfolge aus Cache für Drucker {printer_id}: {cached_order}")
                return cached_order
            
            # Aus Datenbank laden
            job_ids = JobOrder.get_ordered_job_ids(printer_id)
            
            # Cache aktualisieren
            self.job_order_cache[printer_id] = job_ids
            
            logger.debug(f"Job-Reihenfolge aus Datenbank geladen für Drucker {printer_id}: {job_ids}")
            return job_ids
            
        except Exception as e:
            logger.error(f"Fehler beim Laden der Job-Reihenfolge für Drucker {printer_id}: {str(e)}")
            return []
    
    def get_ordered_jobs_for_printer(self, printer_id: int) -> List[Job]:
        """
        Holt die Jobs für einen Drucker in der korrekten Reihenfolge.
        
        Args:
            printer_id: ID des Druckers
        
        Returns:
            List[Job]: Jobs sortiert nach der benutzerdefinierten Reihenfolge
        """
        try:
            # Job-IDs in der korrekten Reihenfolge holen
            ordered_job_ids = self.get_job_order(printer_id)
            
            if not ordered_job_ids:
                # Fallback: Jobs nach Standard-Kriterien sortieren
                with get_db_session() as db_session:
                    jobs = db_session.query(Job).filter(
                        Job.printer_id == printer_id,
                        Job.status.in_(['scheduled', 'paused'])
                    ).order_by(Job.created_at).all()
                    return jobs
            
            # Jobs in der definierten Reihenfolge laden
            with get_db_session() as db_session:
                # Alle relevanten Jobs laden
                all_jobs = db_session.query(Job).filter(
                    Job.printer_id == printer_id,
                    Job.status.in_(['scheduled', 'paused'])
                ).all()
                
                # Dictionary für schnelle Zugriffe
                jobs_dict = {job.id: job for job in all_jobs}
                
                # Jobs in der korrekten Reihenfolge zusammenstellen
                ordered_jobs = []
                for job_id in ordered_job_ids:
                    if job_id in jobs_dict:
                        ordered_jobs.append(jobs_dict[job_id])
                
                # Jobs hinzufügen, die nicht in der Reihenfolge sind (neue Jobs)
                ordered_job_ids_set = set(ordered_job_ids)
                unordered_jobs = [job for job in all_jobs if job.id not in ordered_job_ids_set]
                
                if unordered_jobs:
                    # Neue Jobs nach Erstellungsdatum sortieren und anhängen
                    unordered_jobs.sort(key=lambda x: x.created_at)
                    ordered_jobs.extend(unordered_jobs)
                    
                    # Reihenfolge automatisch aktualisieren für neue Jobs
                    new_order = [job.id for job in ordered_jobs]
                    self.update_job_order(printer_id, new_order)
                
                logger.debug(f"Jobs für Drucker {printer_id} in Reihenfolge geladen: {len(ordered_jobs)} Jobs")
                return ordered_jobs
                
        except Exception as e:
            logger.error(f"Fehler beim Laden der sortierten Jobs für Drucker {printer_id}: {str(e)}")
            
            # Fallback: Unsortierte Jobs zurückgeben
            try:
                with get_db_session() as db_session:
                    jobs = db_session.query(Job).filter(
                        Job.printer_id == printer_id,
                        Job.status.in_(['scheduled', 'paused'])
                    ).order_by(Job.created_at).all()
                    return jobs
            except Exception as fallback_error:
                logger.error(f"Auch Fallback fehlgeschlagen: {str(fallback_error)}")
                return []
    
    def remove_job_from_order(self, job_id: int) -> bool:
        """
        Entfernt einen Job aus allen Drucker-Reihenfolgen.
        
        Args:
            job_id: ID des zu entfernenden Jobs
        
        Returns:
            bool: True wenn erfolgreich
        """
        try:
            # Aus Datenbank entfernen
            JobOrder.remove_job_from_orders(job_id)
            
            # Cache aktualisieren: Job aus allen Caches entfernen
            for printer_id in list(self.job_order_cache.keys()):
                if job_id in self.job_order_cache[printer_id]:
                    self.job_order_cache[printer_id].remove(job_id)
                    logger.debug(f"Job {job_id} aus Cache für Drucker {printer_id} entfernt")
            
            logger.info(f"Job {job_id} erfolgreich aus allen Reihenfolgen entfernt")
            return True
            
        except Exception as e:
            logger.error(f"Fehler beim Entfernen des Jobs {job_id} aus Reihenfolgen: {str(e)}")
            return False
    
    def cleanup_invalid_orders(self):
        """Bereinigt ungültige Job-Reihenfolgen"""
        try:
            # Datenbank-Bereinigung
            JobOrder.cleanup_invalid_orders()
            
            # Cache komplett leeren (wird bei Bedarf neu geladen)
            self.job_order_cache.clear()
            
            logger.info("Job-Reihenfolgen bereinigt")
            
        except Exception as e:
            logger.error(f"Fehler bei der Bereinigung der Job-Reihenfolgen: {str(e)}")
    
    def _schedule_cleanup(self):
        """Plant eine Bereinigung für später (non-blocking)"""
        try:
            # In produktiver Umgebung könnte hier ein Background-Task gestartet werden
            # Für jetzt führen wir eine schnelle Bereinigung durch
            import threading
            
            def cleanup_worker():
                try:
                    self.cleanup_invalid_orders()
                except Exception as e:
                    logger.error(f"Hintergrund-Bereinigung fehlgeschlagen: {str(e)}")
            
            cleanup_thread = threading.Thread(target=cleanup_worker, daemon=True)
            cleanup_thread.start()
            
        except Exception as e:
            logger.debug(f"Konnte Hintergrund-Bereinigung nicht starten: {str(e)}")
    
    def get_printer_summary(self, printer_id: int) -> Dict[str, Any]:
        """
        Erstellt eine Zusammenfassung der Job-Reihenfolge für einen Drucker.
        
        Args:
            printer_id: ID des Druckers
        
        Returns:
            Dict: Zusammenfassung mit Jobs, Reihenfolge, Statistiken
        """
        try:
            ordered_jobs = self.get_ordered_jobs_for_printer(printer_id)
            
            # Statistiken berechnen
            total_duration = sum(job.duration_minutes for job in ordered_jobs)
            total_jobs = len(ordered_jobs)
            
            # Nächster Job
            next_job = ordered_jobs[0] if ordered_jobs else None
            
            # Job-Details für die Ausgabe
            job_details = []
            for position, job in enumerate(ordered_jobs):
                job_details.append({
                    'position': position,
                    'job_id': job.id,
                    'name': job.name,
                    'duration_minutes': job.duration_minutes,
                    'user_name': job.user.name if job.user else 'Unbekannt',
                    'created_at': job.created_at.isoformat() if job.created_at else None,
                    'status': job.status
                })
            
            return {
                'printer_id': printer_id,
                'total_jobs': total_jobs,
                'total_duration_minutes': total_duration,
                'estimated_completion': self._calculate_completion_time(ordered_jobs),
                'next_job': {
                    'id': next_job.id,
                    'name': next_job.name,
                    'user': next_job.user.name if next_job and next_job.user else None
                } if next_job else None,
                'jobs': job_details,
                'last_updated': datetime.now().isoformat()
            }
            
        except Exception as e:
            logger.error(f"Fehler beim Erstellen der Drucker-Zusammenfassung für {printer_id}: {str(e)}")
            return {
                'printer_id': printer_id,
                'total_jobs': 0,
                'total_duration_minutes': 0,
                'error': str(e)
            }
    
    def _calculate_completion_time(self, jobs: List[Job]) -> Optional[str]:
        """Berechnet die geschätzte Fertigstellungszeit"""
        try:
            if not jobs:
                return None
            
            total_minutes = sum(job.duration_minutes for job in jobs)
            completion_time = datetime.now()
            completion_time = completion_time.replace(
                minute=(completion_time.minute + total_minutes) % 60,
                hour=(completion_time.hour + (completion_time.minute + total_minutes) // 60) % 24
            )
            
            return completion_time.isoformat()
            
        except Exception:
            return None

# Globale Instanz
drag_drop_manager = DragDropManager()

def validate_file_upload(file_data: Dict[str, Any], config: DragDropConfig) -> Tuple[bool, str]:
    """Validiert eine Datei für Upload"""
    filename = file_data.get('name', '')
    file_size = file_data.get('size', 0)
    file_type = file_data.get('type', '')
    
    # Dateiname prüfen
    if not filename:
        return False, "Dateiname ist erforderlich"
    
    # Dateierweiterung prüfen
    file_extension = filename.rsplit('.', 1)[-1].lower() if '.' in filename else ''
    if file_extension not in config.accepted_types:
        return False, f"Dateityp '{file_extension}' nicht erlaubt. Erlaubt: {', '.join(config.accepted_types)}"
    
    # Dateigröße prüfen
    if file_size > config.max_file_size:
        max_mb = config.max_file_size / (1024 * 1024)
        return False, f"Datei zu groß. Maximum: {max_mb:.1f} MB"
    
    if file_size <= 0:
        return False, "Datei ist leer"
    
    # MIME-Type validieren
    expected_mime = mimetypes.guess_type(filename)[0]
    if expected_mime and file_type and not file_type.startswith(expected_mime.split('/')[0]):
        return False, "MIME-Type stimmt nicht mit Dateierweiterung überein"
    
    # Custom Validierung
    if config.validation_rules:
        for rule_name, rule_value in config.validation_rules.items():
            if rule_name == 'min_size' and file_size < rule_value:
                return False, f"Datei zu klein. Minimum: {rule_value} Bytes"
            elif rule_name == 'max_name_length' and len(filename) > rule_value:
                return False, f"Dateiname zu lang. Maximum: {rule_value} Zeichen"
    
    return True, ""

def get_drag_drop_javascript() -> str:
    """Generiert JavaScript für Drag & Drop Funktionalität"""
    return """
    class DragDropManager {
        constructor() {
            this.dropZones = new Map();
            this.uploadSessions = new Map();
            this.sortableContainers = new Map();
            
            this.setupGlobalEventListeners();
        }
        
        setupGlobalEventListeners() {
            // Verhindere Standard-Drag-Verhalten für das gesamte Dokument
            document.addEventListener('dragover', (e) => {
                e.preventDefault();
            });
            
            document.addEventListener('drop', (e) => {
                e.preventDefault();
            });
        }
        
        createDropZone(elementId, config = {}) {
            const element = document.getElementById(elementId);
            if (!element) {
                console.error(`Drop-Zone Element nicht gefunden: ${elementId}`);
                return null;
            }
            
            const dropZone = new DropZone(element, config);
            this.dropZones.set(elementId, dropZone);
            
            return dropZone;
        }
        
        createSortableContainer(elementId, config = {}) {
            const element = document.getElementById(elementId);
            if (!element) {
                console.error(`Sortable Container nicht gefunden: ${elementId}`);
                return null;
            }
            
            const sortable = new SortableContainer(element, config);
            this.sortableContainers.set(elementId, sortable);
            
            return sortable;
        }
        
        createUploadSession(sessionId) {
            const session = new UploadSession(sessionId);
            this.uploadSessions.set(sessionId, session);
            return session;
        }
        
        getUploadSession(sessionId) {
            return this.uploadSessions.get(sessionId);
        }
    }
    
    class DropZone {
        constructor(element, config = {}) {
            this.element = element;
            this.config = {
                acceptedTypes: ['*'],
                maxFiles: 10,
                maxFileSize: 10 * 1024 * 1024, // 10MB
                multiple: true,
                autoUpload: false,
                previewMode: 'thumbnail',
                uploadUrl: '/api/upload/drag-drop',
                onFilesAdded: null,
                onUploadProgress: null,
                onUploadComplete: null,
                onError: null,
                ...config
            };
            
            this.files = [];
            this.dragCounter = 0;
            
            this.setupElement();
            this.setupEventListeners();
        }
        
        setupElement() {
            this.element.classList.add('drag-drop-zone');
            
            if (!this.element.innerHTML.trim()) {
                this.element.innerHTML = `
                    <div class="drop-zone-content">
                        <div class="drop-zone-icon">📁</div>
                        <div class="drop-zone-text">
                            Dateien hierher ziehen oder 
                            <button type="button" class="file-select-btn">durchsuchen</button>
                        </div>
                        <div class="drop-zone-info">
                            ${this.getAcceptedTypesText()}
                        </div>
                    </div>
                    <div class="file-preview-container"></div>
                    <input type="file" class="file-input" style="display: none;" 
                           ${this.config.multiple ? 'multiple' : ''} 
                           accept="${this.getAcceptAttribute()}">
                `;
            }
            
            this.fileInput = this.element.querySelector('.file-input');
            this.previewContainer = this.element.querySelector('.file-preview-container');
            this.selectButton = this.element.querySelector('.file-select-btn');
        }
        
        setupEventListeners() {
            // Drag & Drop Events
            this.element.addEventListener('dragenter', (e) => {
                e.preventDefault();
                this.dragCounter++;
                this.element.classList.add('drag-over');
            });
            
            this.element.addEventListener('dragleave', (e) => {
                e.preventDefault();
                this.dragCounter--;
                if (this.dragCounter === 0) {
                    this.element.classList.remove('drag-over');
                }
            });
            
            this.element.addEventListener('dragover', (e) => {
                e.preventDefault();
            });
            
            this.element.addEventListener('drop', (e) => {
                e.preventDefault();
                this.dragCounter = 0;
                this.element.classList.remove('drag-over');
                
                const files = Array.from(e.dataTransfer.files);
                this.handleFiles(files);
            });
            
            // File Input Events
            this.selectButton?.addEventListener('click', () => {
                this.fileInput.click();
            });
            
            this.fileInput.addEventListener('change', (e) => {
                const files = Array.from(e.target.files);
                this.handleFiles(files);
            });
            
            // Keyboard Support
            this.selectButton?.addEventListener('keydown', (e) => {
                if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    this.fileInput.click();
                }
            });
        }
        
        handleFiles(files) {
            // Validierung
            const validFiles = [];
            const errors = [];
            
            for (const file of files) {
                const validation = this.validateFile(file);
                if (validation.valid) {
                    validFiles.push(file);
                } else {
                    errors.push(`${file.name}: ${validation.error}`);
                }
            }
            
            // Fehler anzeigen
            if (errors.length > 0) {
                this.showErrors(errors);
            }
            
            // Gültige Dateien hinzufügen
            if (validFiles.length > 0) {
                this.addFiles(validFiles);
            }
        }
        
        validateFile(file) {
            // Anzahl Dateien prüfen
            if (!this.config.multiple && this.files.length >= 1) {
                return { valid: false, error: 'Nur eine Datei erlaubt' };
            }
            
            if (this.files.length >= this.config.maxFiles) {
                return { valid: false, error: `Maximum ${this.config.maxFiles} Dateien erlaubt` };
            }
            
            // Dateigröße prüfen
            if (file.size > this.config.maxFileSize) {
                const maxMB = this.config.maxFileSize / (1024 * 1024);
                return { valid: false, error: `Datei zu groß. Maximum: ${maxMB.toFixed(1)} MB` };
            }
            
            // Dateityp prüfen
            if (!this.isFileTypeAccepted(file)) {
                return { valid: false, error: `Dateityp nicht erlaubt: ${file.type}` };
            }
            
            return { valid: true };
        }
        
        isFileTypeAccepted(file) {
            if (this.config.acceptedTypes.includes('*')) {
                return true;
            }
            
            const fileExtension = file.name.split('.').pop().toLowerCase();
            return this.config.acceptedTypes.includes(fileExtension) ||
                   this.config.acceptedTypes.some(type => file.type.includes(type));
        }
        
        addFiles(files) {
            files.forEach(file => {
                const fileObj = {
                    id: this.generateFileId(),
                    file: file,
                    name: file.name,
                    size: file.size,
                    type: file.type,
                    preview: null,
                    uploadProgress: 0,
                    status: 'pending' // pending, uploading, completed, error
                };
                
                this.files.push(fileObj);
                this.createFilePreview(fileObj);
            });
            
            this.updateUI();
            
            // Callback aufrufen
            if (this.config.onFilesAdded) {
                this.config.onFilesAdded(files, this.files);
            }
            
            // Auto-Upload
            if (this.config.autoUpload) {
                this.uploadFiles();
            }
        }
        
        createFilePreview(fileObj) {
            const preview = document.createElement('div');
            preview.className = 'file-preview';
            preview.dataset.fileId = fileObj.id;
            
            // Preview basierend auf Modus
            if (this.config.previewMode === 'thumbnail') {
                preview.innerHTML = this.createThumbnailPreview(fileObj);
            } else {
                preview.innerHTML = this.createListPreview(fileObj);
            }
            
            this.previewContainer.appendChild(preview);
            
            // Remove Button
            const removeBtn = preview.querySelector('.remove-file-btn');
            removeBtn?.addEventListener('click', () => {
                this.removeFile(fileObj.id);
            });
            
            // Thumbnail generieren (für Bilder)
            if (fileObj.file.type.startsWith('image/')) {
                this.generateThumbnail(fileObj, preview);
            }
        }
        
        createThumbnailPreview(fileObj) {
            return `
                <div class="file-thumbnail">
                    <div class="file-icon" data-type="${fileObj.type}">
                        ${this.getFileIcon(fileObj.type)}
                    </div>
                    <div class="file-info">
                        <div class="file-name" title="${fileObj.name}">${fileObj.name}</div>
                        <div class="file-size">${this.formatFileSize(fileObj.size)}</div>
                        <div class="file-progress">
                            <div class="progress-bar">
                                <div class="progress-fill" style="width: ${fileObj.uploadProgress}%"></div>
                            </div>
                            <span class="progress-text">${fileObj.uploadProgress}%</span>
                        </div>
                    </div>
                    <button class="remove-file-btn" type="button" aria-label="Datei entfernen">×</button>
                </div>
            `;
        }
        
        createListPreview(fileObj) {
            return `
                <div class="file-list-item">
                    <span class="file-icon">${this.getFileIcon(fileObj.type)}</span>
                    <span class="file-name">${fileObj.name}</span>
                    <span class="file-size">${this.formatFileSize(fileObj.size)}</span>
                    <div class="file-progress">
                        <div class="progress-bar">
                            <div class="progress-fill" style="width: ${fileObj.uploadProgress}%"></div>
                        </div>
                    </div>
                    <button class="remove-file-btn" type="button">Entfernen</button>
                </div>
            `;
        }
        
        generateThumbnail(fileObj, previewElement) {
            const reader = new FileReader();
            reader.onload = (e) => {
                const img = previewElement.querySelector('.file-icon');
                if (img) {
                    img.innerHTML = `<img src="${e.target.result}" alt="${fileObj.name}" style="width: 100%; height: 100%; object-fit: cover;">`;
                }
            };
            reader.readAsDataURL(fileObj.file);
        }
        
        getFileIcon(fileType) {
            if (fileType.startsWith('image/')) return '🖼️';
            if (fileType.startsWith('video/')) return '🎬';
            if (fileType.startsWith('audio/')) return '🎵';
            if (fileType.includes('pdf')) return '📄';
            if (fileType.includes('text/') || fileType.includes('document')) return '📝';
            if (fileType.includes('zip') || fileType.includes('archive')) return '🗜️';
            return '📁';
        }
        
        formatFileSize(bytes) {
            if (bytes === 0) return '0 Bytes';
            
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }
        
        removeFile(fileId) {
            this.files = this.files.filter(f => f.id !== fileId);
            
            const preview = this.previewContainer.querySelector(`[data-file-id="${fileId}"]`);
            if (preview) {
                preview.remove();
            }
            
            this.updateUI();
        }
        
        updateUI() {
            // UI-Updates basierend auf Dateien-Status
            if (this.files.length === 0) {
                this.element.classList.remove('has-files');
            } else {
                this.element.classList.add('has-files');
            }
        }
        
        async uploadFiles() {
            const sessionId = this.generateSessionId();
            
            for (const fileObj of this.files) {
                if (fileObj.status === 'pending') {
                    await this.uploadFile(fileObj, sessionId);
                }
            }
        }
        
        async uploadFile(fileObj, sessionId) {
            fileObj.status = 'uploading';
            
            const formData = new FormData();
            formData.append('file', fileObj.file);
            formData.append('session_id', sessionId);
            formData.append('file_id', fileObj.id);
            
            try {
                const response = await fetch(this.config.uploadUrl, {
                    method: 'POST',
                    body: formData,
                    // Progress tracking würde XMLHttpRequest benötigen
                });
                
                if (response.ok) {
                    const result = await response.json();
                    fileObj.status = 'completed';
                    fileObj.uploadProgress = 100;
                    fileObj.uploadResult = result;
                    
                    this.updateFilePreview(fileObj);
                    
                    if (this.config.onUploadComplete) {
                        this.config.onUploadComplete(fileObj, result);
                    }
                } else {
                    throw new Error(`Upload fehlgeschlagen: ${response.status}`);
                }
                
            } catch (error) {
                fileObj.status = 'error';
                fileObj.error = error.message;
                
                this.updateFilePreview(fileObj);
                
                if (this.config.onError) {
                    this.config.onError(fileObj, error);
                }
            }
        }
        
        updateFilePreview(fileObj) {
            const preview = this.previewContainer.querySelector(`[data-file-id="${fileObj.id}"]`);
            if (preview) {
                const progressFill = preview.querySelector('.progress-fill');
                const progressText = preview.querySelector('.progress-text');
                
                if (progressFill) {
                    progressFill.style.width = `${fileObj.uploadProgress}%`;
                }
                
                if (progressText) {
                    progressText.textContent = `${fileObj.uploadProgress}%`;
                }
                
                preview.classList.remove('status-pending', 'status-uploading', 'status-completed', 'status-error');
                preview.classList.add(`status-${fileObj.status}`);
            }
        }
        
        getAcceptedTypesText() {
            if (this.config.acceptedTypes.includes('*')) {
                return 'Alle Dateitypen erlaubt';
            }
            return `Erlaubt: ${this.config.acceptedTypes.join(', ')}`;
        }
        
        getAcceptAttribute() {
            if (this.config.acceptedTypes.includes('*')) {
                return '*/*';
            }
            return this.config.acceptedTypes.map(type => `.${type}`).join(',');
        }
        
        showErrors(errors) {
            const errorContainer = document.createElement('div');
            errorContainer.className = 'upload-errors';
            errorContainer.innerHTML = `
                <div class="error-title">Upload-Fehler:</div>
                <ul>
                    ${errors.map(error => `<li>${error}</li>`).join('')}
                </ul>
                <button class="close-errors-btn">Schließen</button>
            `;
            
            this.element.appendChild(errorContainer);
            
            errorContainer.querySelector('.close-errors-btn').addEventListener('click', () => {
                errorContainer.remove();
            });
            
            // Auto-remove nach 5 Sekunden
            setTimeout(() => {
                if (errorContainer.parentNode) {
                    errorContainer.remove();
                }
            }, 5000);
        }
        
        generateFileId() {
            return 'file_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
        }
        
        generateSessionId() {
            return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
        }
    }
    
    class SortableContainer {
        constructor(element, config = {}) {
            this.element = element;
            this.config = {
                handle: null,
                placeholder: 'sortable-placeholder',
                ghostClass: 'sortable-ghost',
                chosenClass: 'sortable-chosen',
                dragClass: 'sortable-drag',
                onUpdate: null,
                onSort: null,
                disabled: false,
                animation: 150,
                ...config
            };
            
            this.dragElement = null;
            this.placeholder = null;
            this.isDragging = false;
            this.startIndex = -1;
            this.endIndex = -1;
            
            this.setupSortable();
        }
        
        setupSortable() {
            this.element.classList.add('sortable-container');
            
            // Event Listeners für alle draggable Items
            this.updateEventListeners();
        }
        
        updateEventListeners() {
            const items = this.getSortableItems();
            
            items.forEach((item, index) => {
                item.draggable = true;
                item.dataset.sortableIndex = index;
                
                // Remove existing listeners
                item.removeEventListener('dragstart', this.handleDragStart);
                item.removeEventListener('dragend', this.handleDragEnd);
                item.removeEventListener('dragover', this.handleDragOver);
                item.removeEventListener('drop', this.handleDrop);
                
                // Add new listeners
                item.addEventListener('dragstart', this.handleDragStart.bind(this));
                item.addEventListener('dragend', this.handleDragEnd.bind(this));
                item.addEventListener('dragover', this.handleDragOver.bind(this));
                item.addEventListener('drop', this.handleDrop.bind(this));
            });
        }
        
        getSortableItems() {
            return Array.from(this.element.children).filter(child => 
                !child.classList.contains('sortable-placeholder') &&
                !child.classList.contains('non-sortable')
            );
        }
        
        handleDragStart(e) {
            if (this.config.disabled) {
                e.preventDefault();
                return;
            }
            
            this.isDragging = true;
            this.dragElement = e.target.closest('[draggable="true"]');
            this.startIndex = parseInt(this.dragElement.dataset.sortableIndex);
            
            this.dragElement.classList.add(this.config.chosenClass);
            
            // Create placeholder
            this.createPlaceholder();
            
            // Set drag data
            e.dataTransfer.effectAllowed = 'move';
            e.dataTransfer.setData('text/html', this.dragElement.outerHTML);
            
            // Add ghost class after a delay to allow proper drag image
            setTimeout(() => {
                if (this.dragElement) {
                    this.dragElement.classList.add(this.config.ghostClass);
                }
            }, 0);
        }
        
        handleDragEnd(e) {
            if (!this.isDragging) return;
            
            this.isDragging = false;
            
            // Clean up classes
            if (this.dragElement) {
                this.dragElement.classList.remove(
                    this.config.chosenClass,
                    this.config.ghostClass,
                    this.config.dragClass
                );
            }
            
            // Remove placeholder
            this.removePlaceholder();
            
            // Update positions
            this.endIndex = this.findElementIndex(this.dragElement);
            
            if (this.startIndex !== this.endIndex) {
                // Trigger callbacks
                if (this.config.onUpdate) {
                    this.config.onUpdate({
                        item: this.dragElement,
                        oldIndex: this.startIndex,
                        newIndex: this.endIndex,
                        from: this.element,
                        to: this.element
                    });
                }
                
                if (this.config.onSort) {
                    this.config.onSort(this.getSortOrder());
                }
            }
            
            // Reset
            this.dragElement = null;
            this.startIndex = -1;
            this.endIndex = -1;
            
            // Update event listeners for new order
            this.updateEventListeners();
        }
        
        handleDragOver(e) {
            if (!this.isDragging) return;
            
            e.preventDefault();
            e.dataTransfer.dropEffect = 'move';
            
            const afterElement = this.getDragAfterElement(e.clientY);
            
            if (afterElement == null) {
                this.element.appendChild(this.placeholder);
            } else {
                this.element.insertBefore(this.placeholder, afterElement);
            }
        }
        
        handleDrop(e) {
            if (!this.isDragging) return;
            
            e.preventDefault();
            
            // Insert the dragged element at the placeholder position
            if (this.placeholder && this.dragElement) {
                this.element.insertBefore(this.dragElement, this.placeholder);
            }
        }
        
        createPlaceholder() {
            this.placeholder = document.createElement('div');
            this.placeholder.className = this.config.placeholder;
            this.placeholder.style.height = this.dragElement.offsetHeight + 'px';
            this.placeholder.innerHTML = '<div class="placeholder-content">Drop hier einfügen</div>';
        }
        
        removePlaceholder() {
            if (this.placeholder && this.placeholder.parentNode) {
                this.placeholder.parentNode.removeChild(this.placeholder);
            }
            this.placeholder = null;
        }
        
        getDragAfterElement(y) {
            const draggableElements = [...this.element.querySelectorAll('[draggable="true"]:not(.sortable-ghost)')];
            
            return draggableElements.reduce((closest, child) => {
                const box = child.getBoundingClientRect();
                const offset = y - box.top - box.height / 2;
                
                if (offset < 0 && offset > closest.offset) {
                    return { offset: offset, element: child };
                } else {
                    return closest;
                }
            }, { offset: Number.NEGATIVE_INFINITY }).element;
        }
        
        findElementIndex(element) {
            const items = this.getSortableItems();
            return items.indexOf(element);
        }
        
        getSortOrder() {
            return this.getSortableItems().map(item => ({
                element: item,
                id: item.dataset.id || item.id,
                index: this.findElementIndex(item)
            }));
        }
        
        disable() {
            this.config.disabled = true;
            this.element.classList.add('sortable-disabled');
        }
        
        enable() {
            this.config.disabled = false;
            this.element.classList.remove('sortable-disabled');
        }
    }
    
    class UploadSession {
        constructor(sessionId) {
            this.sessionId = sessionId;
            this.files = new Map();
            this.totalSize = 0;
            this.uploadedSize = 0;
        }
        
        addFile(fileId, fileSize) {
            this.files.set(fileId, {
                id: fileId,
                size: fileSize,
                uploaded: 0,
                status: 'pending'
            });
            this.totalSize += fileSize;
        }
        
        updateFileProgress(fileId, uploadedBytes, status = null) {
            const file = this.files.get(fileId);
            if (file) {
                const previousUploaded = file.uploaded;
                file.uploaded = uploadedBytes;
                
                if (status) {
                    file.status = status;
                }
                
                // Update total uploaded
                this.uploadedSize += (uploadedBytes - previousUploaded);
            }
        }
        
        getProgress() {
            return {
                totalSize: this.totalSize,
                uploadedSize: this.uploadedSize,
                percentage: this.totalSize > 0 ? (this.uploadedSize / this.totalSize) * 100 : 0,
                files: Array.from(this.files.values())
            };
        }
    }
    
    // CSS für Drag & Drop (als String für Injection)
    const dragDropCSS = `
        .drag-drop-zone {
            border: 2px dashed #d1d5db;
            border-radius: 8px;
            padding: 2rem;
            text-align: center;
            transition: all 0.3s ease;
            background-color: #f9fafb;
            position: relative;
        }
        
        .drag-drop-zone.drag-over {
            border-color: #3b82f6;
            background-color: #eff6ff;
            transform: scale(1.02);
        }
        
        .drag-drop-zone.has-files {
            border-style: solid;
            background-color: white;
        }
        
        .drop-zone-content {
            margin-bottom: 1rem;
        }
        
        .drop-zone-icon {
            font-size: 3rem;
            margin-bottom: 1rem;
        }
        
        .drop-zone-text {
            font-size: 1.1rem;
            color: #374151;
            margin-bottom: 0.5rem;
        }
        
        .file-select-btn {
            color: #3b82f6;
            text-decoration: underline;
            background: none;
            border: none;
            cursor: pointer;
            font-size: inherit;
        }
        
        .file-select-btn:hover {
            color: #1d4ed8;
        }
        
        .drop-zone-info {
            font-size: 0.875rem;
            color: #6b7280;
        }
        
        .file-preview-container {
            margin-top: 1rem;
        }
        
        .file-preview {
            margin-bottom: 0.5rem;
            animation: slideIn 0.3s ease;
        }
        
        .file-thumbnail {
            display: flex;
            align-items: center;
            background: white;
            border: 1px solid #e5e7eb;
            border-radius: 6px;
            padding: 0.75rem;
            position: relative;
        }
        
        .file-icon {
            width: 40px;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.5rem;
            margin-right: 0.75rem;
            border-radius: 4px;
            background: #f3f4f6;
        }
        
        .file-info {
            flex: 1;
            min-width: 0;
        }
        
        .file-name {
            font-weight: 500;
            color: #111827;
            truncate: true;
            margin-bottom: 0.25rem;
        }
        
        .file-size {
            font-size: 0.875rem;
            color: #6b7280;
            margin-bottom: 0.5rem;
        }
        
        .file-progress {
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }
        
        .progress-bar {
            flex: 1;
            height: 4px;
            background: #e5e7eb;
            border-radius: 2px;
            overflow: hidden;
        }
        
        .progress-fill {
            height: 100%;
            background: #3b82f6;
            transition: width 0.3s ease;
        }
        
        .progress-text {
            font-size: 0.75rem;
            color: #6b7280;
            min-width: 3rem;
            text-align: right;
        }
        
        .remove-file-btn {
            position: absolute;
            top: 0.5rem;
            right: 0.5rem;
            width: 1.5rem;
            height: 1.5rem;
            border: none;
            background: #ef4444;
            color: white;
            border-radius: 50%;
            cursor: pointer;
            font-size: 1rem;
            line-height: 1;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        
        .remove-file-btn:hover {
            background: #dc2626;
        }
        
        .file-preview.status-completed .progress-fill {
            background: #10b981;
        }
        
        .file-preview.status-error .progress-fill {
            background: #ef4444;
        }
        
        .file-preview.status-error {
            border-color: #fecaca;
            background: #fef2f2;
        }
        
        .upload-errors {
            margin-top: 1rem;
            padding: 1rem;
            background: #fef2f2;
            border: 1px solid #fecaca;
            border-radius: 6px;
            color: #dc2626;
        }
        
        .error-title {
            font-weight: 600;
            margin-bottom: 0.5rem;
        }
        
        .close-errors-btn {
            margin-top: 0.5rem;
            padding: 0.25rem 0.5rem;
            background: #dc2626;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 0.875rem;
        }
        
        /* Sortable Styles */
        .sortable-container {
            min-height: 2rem;
        }
        
        .sortable-container [draggable="true"] {
            cursor: move;
            transition: all 0.15s ease;
        }
        
        .sortable-container [draggable="true"]:hover {
            transform: translateY(-1px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }
        
        .sortable-placeholder {
            background: #eff6ff;
            border: 2px dashed #3b82f6;
            border-radius: 6px;
            margin: 0.25rem 0;
            opacity: 0.8;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #3b82f6;
            font-weight: 500;
        }
        
        .placeholder-content {
            padding: 1rem;
        }
        
        .sortable-ghost {
            opacity: 0.4;
            transform: scale(0.98);
        }
        
        .sortable-chosen {
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        }
        
        .sortable-disabled [draggable="true"] {
            cursor: not-allowed;
            opacity: 0.6;
        }
        
        @keyframes slideIn {
            from {
                opacity: 0;
                transform: translateY(-10px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
        
        /* Mobile Touch Support */
        @media (max-width: 768px) {
            .drag-drop-zone {
                padding: 1rem;
            }
            
            .drop-zone-text {
                font-size: 1rem;
            }
            
            .file-thumbnail {
                padding: 0.5rem;
            }
            
            .file-icon {
                width: 32px;
                height: 32px;
                font-size: 1.25rem;
            }
        }
    `;
    
    // CSS automatisch injizieren
    if (!document.getElementById('drag-drop-styles')) {
        const style = document.createElement('style');
        style.id = 'drag-drop-styles';
        style.textContent = dragDropCSS;
        document.head.appendChild(style);
    }
    
    // Globale Instanz erstellen
    window.dragDropManager = new DragDropManager();
    
    // Auto-Initialize für vorhandene Drop-Zones
    document.addEventListener('DOMContentLoaded', function() {
        // Auto-init für Elemente mit data-drag-drop Attribut
        document.querySelectorAll('[data-drag-drop]').forEach(element => {
            const config = JSON.parse(element.dataset.dragDrop || '{}');
            window.dragDropManager.createDropZone(element.id, config);
        });
        
        // Auto-init für Sortable Container
        document.querySelectorAll('[data-sortable]').forEach(element => {
            const config = JSON.parse(element.dataset.sortable || '{}');
            window.dragDropManager.createSortableContainer(element.id, config);
        });
    });
    """

def get_drag_drop_css() -> str:
    """Gibt CSS für Drag & Drop zurück"""
    return """
    /* Drag & Drop CSS ist bereits im JavaScript enthalten */
    """ 