import subprocess
import asyncio
import aiohttp
from aiohttp import BasicAuth
import aiofiles
import os
from datetime import datetime

def log_error(message, log_file=None):
    """Schreibt Fehler in Konsole und optional in Log-Datei"""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log_message = f"[{timestamp}] {message}"
    print(log_message)
    if log_file:
        try:
            with open(log_file, 'a', encoding='utf-8') as f:
                f.write(log_message + '\n')
        except Exception:
            pass  # Ignoriere Log-Fehler

async def upload_single_file_with_retry(session, path, url, auth, max_retries=3, retry_delay=2):
    """Lädt eine einzelne Datei hoch mit Retry-Mechanismus"""
    for attempt in range(max_retries):
        try:
            if not os.path.exists(path):
                return False, f"Datei nicht gefunden: {path}"
            
            file_size = os.path.getsize(path)
            if file_size == 0:
                return False, f"Datei ist leer: {path}"
            
            # Warnung bei sehr großen Dateien (>100MB)
            if file_size > 100 * 1024 * 1024:
                log_error(f"Warnung: Sehr große Datei ({file_size / 1024 / 1024:.1f}MB): {os.path.basename(path)}")
            
            async with aiofiles.open(path, 'rb') as f:
                content = await f.read()
            
            # Timeout pro Datei: 2 Minuten
            timeout = aiohttp.ClientTimeout(total=120)
            async with session.post(url, data=content, auth=auth, timeout=timeout) as response:
                if response.status == 200:
                    return True, f"Erfolgreich hochgeladen: {os.path.basename(path)}"
                else:
                    error_text = await response.text()
                    error_msg = f"Statuscode {response.status} für {os.path.basename(path)}"
                    if error_text:
                        error_msg += f" - Server: {error_text[:200]}"
                    
                    # Bei 4xx Fehlern (Client-Fehler) nicht wiederholen
                    if 400 <= response.status < 500:
                        return False, error_msg
                    
                    # Bei anderen Fehlern: Retry
                    if attempt < max_retries - 1:
                        log_error(f"Versuch {attempt + 1}/{max_retries} fehlgeschlagen: {error_msg}. Wiederhole in {retry_delay}s...")
                        await asyncio.sleep(retry_delay)
                        continue
                    else:
                        return False, error_msg
                        
        except asyncio.TimeoutError:
            error_msg = f"Timeout beim Upload von {os.path.basename(path)}"
            if attempt < max_retries - 1:
                log_error(f"Versuch {attempt + 1}/{max_retries} fehlgeschlagen: {error_msg}. Wiederhole in {retry_delay}s...")
                await asyncio.sleep(retry_delay)
                continue
            else:
                return False, error_msg
                
        except aiohttp.ClientError as e:
            error_msg = f"Netzwerkfehler: {os.path.basename(path)} - {str(e)}"
            if attempt < max_retries - 1:
                log_error(f"Versuch {attempt + 1}/{max_retries} fehlgeschlagen: {error_msg}. Wiederhole in {retry_delay}s...")
                await asyncio.sleep(retry_delay)
                continue
            else:
                return False, error_msg
                
        except OSError as e:
            return False, f"Dateifehler: {os.path.basename(path)} - {str(e)}"
            
        except Exception as e:
            error_msg = f"Unerwarteter Fehler: {os.path.basename(path)} - {str(e)}"
            if attempt < max_retries - 1:
                log_error(f"Versuch {attempt + 1}/{max_retries} fehlgeschlagen: {error_msg}. Wiederhole in {retry_delay}s...")
                await asyncio.sleep(retry_delay)
                continue
            else:
                return False, error_msg
    
    return False, f"Maximale Anzahl Wiederholungen erreicht für {os.path.basename(path)}"

async def upload_multiple_files(paths, url, username, password, success_callback, progress_callback):
        print("Starte Uploads...")
        auth = BasicAuth(login=username, password=password) if username and password else None
        total_files = len(paths)
        print(f"Gesamtanzahl Dateien: {total_files}")
        
        # Log-Datei erstellen
        log_file = os.path.join(os.path.expanduser('~'), 'dicom2pacs_upload.log')
        log_error(f"=== Upload gestartet: {total_files} Dateien ===", log_file)
        
        if progress_callback:
            progress_callback(0, total_files)
        
        # Timeout für die gesamte Session: 30 Minuten
        timeout = aiohttp.ClientTimeout(total=1800)
        async with aiohttp.ClientSession(timeout=timeout) as session:
            uploaded_count = 0
            failed_files = []
            
            for idx, path in enumerate(paths, 1):
                log_error(f"Upload {idx}/{total_files}: {os.path.basename(path)}", log_file)
                success, message = await upload_single_file_with_retry(session, path, url, auth)
                
                if success:
                    uploaded_count += 1
                    log_error(message, log_file)
                    if success_callback is not None:
                        await success_callback(message)
                    if progress_callback:
                        progress_callback(1, total_files)
                else:
                    failed_files.append((os.path.basename(path), message))
                    log_error(f"FEHLER: {message}", log_file)
                    # Fortschritt trotzdem aktualisieren, damit UI nicht hängt
                    if progress_callback:
                        progress_callback(1, total_files)
        
        # Zusammenfassung
        summary = f"Upload abgeschlossen: {uploaded_count}/{total_files} Dateien erfolgreich hochgeladen"
        log_error(summary, log_file)
        
        if failed_files:
            log_error(f"Fehlgeschlagene Dateien ({len(failed_files)}):", log_file)
            for filename, error in failed_files:
                log_error(f"  - {filename}: {error}", log_file)
        
        log_error("=== Upload beendet ===", log_file)
        print(summary)
        
        if failed_files:
            print(f"\n⚠️  {len(failed_files)} Dateien konnten nicht hochgeladen werden.")
            print(f"Details finden Sie in der Log-Datei: {log_file}")
        
        return uploaded_count, len(failed_files)
        
        
async def check_server_availability(url):
    """
    Überprüft die Verfügbarkeit des Servers durch Pingen der IP-Adresse.

    :param url: URL des Servers, dessen Verfügbarkeit überprüft werden soll
    :return: True, wenn der Server erreichbar ist, sonst False
    """
    if not url:
        print("Keine URL angegeben")
        return False
    
    try:
        # Validierung der URL
        if "//" not in url:
            print(f"Ungültige URL: {url}")
            return False
        
        # Extrahieren des Host-Teils (IP-Adresse) aus der URL
        base_url = url.split("//")[1].split("/")[0]

        # Extrahieren der IP-Adresse aus dem Host-Teil
        ip_address = base_url.split(":")[0]
        
        if not ip_address:
            print("Konnte IP-Adresse nicht aus URL extrahieren")
            return False

        print(f"Trying to ping {ip_address}...")  # Debug-Print

        # Verwende asyncio.create_subprocess_exec, um den ping-Befehl asynchron auszuführen
        # Timeout von 5 Sekunden für ping
        process = await asyncio.create_subprocess_exec(
            "ping", "-c", "1", "-W", "5000", ip_address,
            stdout=subprocess.PIPE, 
            stderr=subprocess.PIPE
        )
        try:
            _, _ = await asyncio.wait_for(process.communicate(), timeout=10)
        except asyncio.TimeoutError:
            process.kill()
            await process.wait()
            print(f"Ping timeout für {ip_address}")
            return False

        if process.returncode == 0:
            print(f"Ping successful to {ip_address}")  # Debug-Print
            return True
        else:
            print(f"Ping failed to {ip_address}")  # Debug-Print
            return False
    except ValueError as e:
        print(f"Ungültige URL-Format: {e}")
        return False
    except Exception as e:
        print(f"Exception occurred: {str(e)}")  # Debug-Print
        return False
