import subprocess
import json
import os
import shutil
import re

def run_shell(command, return_output=False, capture_output=False, stream_output=False):
    """Führt einen Shell-Befehl aus und gibt True (oder Output) zurück, wenn erfolgreich.
    
    Args:
        command: Der auszuführende Shell-Befehl
        return_output: Wenn True, wird stdout als String zurückgegeben (raises on error)
        capture_output: Wenn True, wird ein Dict mit stdout und returncode zurückgegeben (no exception raised)
        stream_output: Wenn True, wird Output live gestreamt UND als Dict zurückgegeben (no exception raised)
    
    Note: Only one of return_output, capture_output, or stream_output should be True.
    """
    # Validate that only one mode is enabled
    modes_set = sum([return_output, capture_output, stream_output])
    if modes_set > 1:
        raise ValueError("Only one of return_output, capture_output, or stream_output can be True")
    
    try:
        if return_output:
            result = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            return result.stdout.strip()
        elif stream_output:
            # Stream output in real-time while also capturing it
            # This allows both live progress display and post-processing of output
            # Note: stderr is redirected to stdout to ensure all yt-dlp progress messages
            # (which use stderr) are captured and displayed
            process = None
            try:
                process = subprocess.Popen(
                    command,
                    shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT,
                    text=True,
                    bufsize=1  # Line buffered
                )
                
                output_lines = []
                for line in process.stdout:
                    print(line, end='', flush=True)  # Print live
                    output_lines.append(line)
                
                process.wait()
                
                return {
                    "stdout": ''.join(output_lines),
                    "returncode": process.returncode
                }
            except (subprocess.SubprocessError, OSError, IOError) as e:
                # Ensure process is terminated if an error occurs during streaming
                if process and process.poll() is None:
                    process.terminate()
                    try:
                        process.wait(timeout=5)
                    except subprocess.TimeoutExpired:
                        process.kill()
                print(f"❌ Fehler beim Streaming: {e}")
                raise
        elif capture_output:
            # Note: check=True is intentionally NOT used here - we want to capture output regardless of exit code
            result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
            return {
                "stdout": result.stdout,
                "returncode": result.returncode
            }
        else:
            subprocess.run(command, shell=True, check=True)
            return True
    except subprocess.CalledProcessError as e:
        if return_output:
             print(f"❌ Fehler bei Befehl: {command}\n{e.stderr}")
        else:
             print(f"❌ Fehler bei Befehl: {command}\n{e}")
        raise e

def get_state(job_dir):
    """Liest den aktuellen Status aus der state.json."""
    state_file = os.path.join(job_dir, "state.json")
    if not os.path.exists(state_file):
        return {}
    try:
        with open(state_file, "r") as f:
            return json.load(f)
    except:
        return {}

def save_state(job_dir, new_data):
    """Aktualisiert den Status in der state.json."""
    current = get_state(job_dir)
    current.update(new_data)
    state_file = os.path.join(job_dir, "state.json")
    with open(state_file, "w") as f:
        json.dump(current, f, indent=4)

def check_disk_space(required_gb=30):
    """
    Prüft, ob genügend Speicherplatz auf dem Volume des Home-Verzeichnisses vorhanden ist.
    Gibt True zurück, wenn Platz reicht oder Nutzer 'y' drückt.
    """
    # Wir prüfen das Home-Verzeichnis, da dort meistens gearbeitet wird
    path = os.path.expanduser("~")
    try:
        usage = shutil.disk_usage(path)
        
        # Umrechnung: Bytes / 1024^3 = GB
        free_gb = usage.free / (1024**3)
        
        if free_gb < required_gb:
            print(f"\n⚠️  WARNUNG: Wenig Speicherplatz! Nur noch {free_gb:.1f} GB frei.")
            print(f"   Benötigt werden ca. {required_gb} GB für einen sicheren Workflow.")
            choice = input("   Trotzdem fortfahren? [y/N]: ").strip().lower()
            return choice == 'y'
        
        return True
    except Exception as e:
        print(f"⚠️  Konnte Speicherplatz nicht prüfen: {e}")
        return True

def normalize_video_input(input_str):
    """
    Erkennt Video-IDs und konvertiert sie zu vollständigen URLs.
    
    Wenn der Input bereits eine URL ist, wird sie unverändert zurückgegeben.
    Wenn der Input eine Video-ID ist, wird sie zur entsprechenden URL konvertiert.
    
    Unterstützte Formate:
    - YouTube IDs: 11 Zeichen (Buchstaben, Zahlen, -, _)
    - Vollständige URLs werden durchgereicht
    
    Args:
        input_str: Eingabe vom Benutzer (URL oder ID)
    
    Returns:
        str: Vollständige URL
    
    Examples:
        >>> normalize_video_input("4PDLO-iC3FM")
        "https://www.youtube.com/watch?v=4PDLO-iC3FM"
        
        >>> normalize_video_input("https://www.youtube.com/watch?v=4PDLO-iC3FM")
        "https://www.youtube.com/watch?v=4PDLO-iC3FM"
    """
    input_str = input_str.strip()
    
    # Wenn bereits eine URL (enthält :// oder startet mit www.), durchreichen
    if '://' in input_str or input_str.startswith('www.'):
        return input_str
    
    # YouTube ID Pattern: Genau 11 Zeichen, bestehend aus Buchstaben, Zahlen, - und _
    # YouTube IDs sind case-sensitive und können folgende Zeichen enthalten: A-Z, a-z, 0-9, -, _
    youtube_id_pattern = r'^[A-Za-z0-9_-]{11}$'
    
    if re.match(youtube_id_pattern, input_str):
        # Es ist eine YouTube ID
        return f"https://www.youtube.com/watch?v={input_str}"
    
    # Falls nichts erkannt wurde, geben wir den Input unverändert zurück
    # yt-dlp kann mit vielen Formaten umgehen und wird ggf. einen Fehler werfen
    return input_str