<?php

require_once __DIR__ . '/../../vendor/getid3/getid3.php';

class ProjectsController {
    
    // Bestimmt den korrekten Pfad zum area-Ordner basierend auf der Umgebung
    private static function getAreaPath() {
        // Prüfe ob wir in der Development-Struktur sind (backend/src/Api)
        $devPath = __DIR__ . '/../../../area';
        if (is_dir($devPath)) {
            return '/../../../area';
        }
        
        // Prüfe ob wir in der Deployment-Struktur sind (src/Api)
        $deployPath = __DIR__ . '/../../area';
        if (is_dir($deployPath)) {
            return '/../../area';
        }
        
        // Fallback auf Development-Pfad
        return '/../../../area';
    }
    
    // Zentrale Liste der zu ignorierenden Dateien
    private static function getIgnoredFiles() {
        return [
            '.DS_Store',        // macOS System-Datei
            'Thumbs.db',        // Windows Thumbnail-Cache
            'desktop.ini',      // Windows Desktop-Konfiguration
            '.gitignore',       // Git-Konfiguration
            '.gitkeep',         // Git-Platzhalter
            'config.yaml',      // Konfigurationsdateien
            'config.yml',
            'setup.yaml',
            'setup.yml',
            '.htaccess',        // Apache-Konfiguration
            'index.php',        // PHP-Index (außer in HTML-Ordnern)
            'web.config',       // IIS-Konfiguration
            '.env',             // Environment-Variablen
            '.env.local',
            'README.md',        // Dokumentation
            'readme.txt',
            'license.txt',
            'LICENSE'
        ];
    }

    // Prüft, ob der eingeloggte Client auf den gewünschten Bereich zugreifen darf
    private static function requireClientAccess($clientDir) {
        $user = self::requireAuth();
        // Admins haben grundsätzlich Zugriff, aber prüfe disallowedClients
        if (isset($user['role']) && $user['role'] === 'admin') {
            $adminData = self::getAdminData($user['username']);
            $disallowedClients = $adminData['disallowedClients'] ?? [];
            
            if (in_array($clientDir, $disallowedClients)) {
                http_response_code(403);
                header('Content-Type: application/json');
                echo json_encode([
                    'success' => false,
                    'error' => [
                        'code' => 'FORBIDDEN',
                        'message' => "Zugriff auf Client '{$clientDir}' ist für diesen Administrator nicht erlaubt."
                    ]
                ]);
                exit;
            }
            return $user;
        }
        if ($user['role'] === 'client' && $user['dir'] !== $clientDir) {
            http_response_code(403);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false,
                'error' => [
                    'code' => 'FORBIDDEN',
                    'message' => 'Nicht berechtigt für diesen Bereich.'
                ]
            ]);
            exit;
        }
        return $user;
    }

    // JWT-basierte Authentifizierungsprüfung für alle API-Methoden
    private static function requireAuth() {
        $headers = getallheaders();
        $authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? null;
        if (!$authHeader || !preg_match('/^Bearer\s+(.*)$/i', $authHeader, $matches)) {
            http_response_code(401);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false,
                'error' => [
                    'code' => 'UNAUTHORIZED',
                    'message' => 'Kein gültiges Auth-Token übergeben.'
                ]
            ]);
            exit;
        }
        $jwt = $matches[1];
        require_once __DIR__ . '/../Services/AuthService.php';
        $user = AuthService::verifyJWT($jwt);
        if (!$user) {
            http_response_code(401);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false,
                'error' => [
                    'code' => 'UNAUTHORIZED',
                    'message' => 'Token ungültig oder abgelaufen.'
                ]
            ]);
            exit;
        }
        // Optional: User-Infos für spätere Nutzung zurückgeben
        return $user;
    }
    
    // Prüft ob eine Datei ignoriert werden soll
    private static function shouldIgnoreFile($filename) {
        $ignoredFiles = self::getIgnoredFiles();
        return in_array(strtolower($filename), array_map('strtolower', $ignoredFiles));
    }

    public static function listForClient($clientDir) {
        self::requireClientAccess($clientDir);
        $base = realpath(__DIR__ . self::getAreaPath() . '/' . $clientDir);
        // Lade clients.json, um den Anzeigenamen des Clients zu bekommen
        $loginsFile = __DIR__ . '/../../../storage/data/clients.json';
        $clientDisplay = $clientDir;
        if (file_exists($loginsFile)) {
            $logins = json_decode(file_get_contents($loginsFile), true);
            foreach ($logins as $login) {
                if (isset($login['dir']) && $login['dir'] === $clientDir) {
                    $clientDisplay = $login['dir'];
                    break;
                }
            }
        }
        if (!$base || !is_dir($base)) {
            http_response_code(404);
            echo json_encode([
                'success' => false,
                'error' => [
                    'code' => 'NOT_FOUND',
                    'message' => 'Kundenordner nicht gefunden.'
                ]
            ]);
            return;
        }
        
        // Lade project_order.json falls vorhanden
        $projectOrderFile = $base . '/project_order.json';
        $projectOrder = [];
        if (file_exists($projectOrderFile)) {
            $orderContent = file_get_contents($projectOrderFile);
            $decoded = json_decode($orderContent, true);
            if (is_array($decoded)) {
                $projectOrder = $decoded;
            }
        }
        
        $projects = [];
        $foundProjects = [];
        
        // Sammle alle verfügbaren Projekte
        foreach (scandir($base) as $entry) {
            if ($entry === '.' || $entry === '..') continue;
            $path = $base . '/' . $entry;
            if (is_dir($path)) {
                $setupDir = $path . '/setup';
                $poster = null;
                $logo = null;
                if (is_dir($setupDir)) {
                    foreach (scandir($setupDir) as $file) {
                        if (!$poster && stripos($file, 'poster') === 0 && preg_match('/\.(jpg|jpeg|png|webp)$/i', $file)) {
                            $poster = '/area/' . rawurlencode($clientDir) . '/' . rawurlencode($entry) . '/setup/' . rawurlencode($file);
                        }
                        if (!$logo && stripos($file, 'logo') === 0 && preg_match('/\.(jpg|jpeg|png|webp)$/i', $file)) {
                            $logo = '/area/' . rawurlencode($clientDir) . '/' . rawurlencode($entry) . '/setup/' . rawurlencode($file);
                        }
                        if ($poster && $logo) break;
                    }
                }
                $isHE = (stripos($entry, 'HE_') === 0);
                $foundProjects[$entry] = [
                    'name' => $entry,
                    'path' => $entry,
                    'poster' => $poster,
                    'logo' => $logo,
                    'isHE' => $isHE,
                    'client' => $clientDisplay
                ];
            }
        }
        
        // Sortiere nach project_order.json falls vorhanden
        if (!empty($projectOrder)) {
            // Erst die Projekte in der definierten Reihenfolge
            foreach ($projectOrder as $orderedProject) {
                if (isset($foundProjects[$orderedProject])) {
                    $projects[] = $foundProjects[$orderedProject];
                    unset($foundProjects[$orderedProject]);
                }
            }
            // Neue Projekte (nicht in order-Liste) kommen an den ANFANG
            $remainingProjects = array_values($foundProjects);
            usort($remainingProjects, function($a, $b) {
                return strcmp($a['name'], $b['name']);
            });
            // Neue Projekte VOR die sortierten einfügen
            $projects = array_merge($remainingProjects, $projects);
        } else {
            // Fallback: Alphabetische Sortierung
            $projects = array_values($foundProjects);
            usort($projects, function($a, $b) {
                return strcmp($a['name'], $b['name']);
            });
        }
        echo json_encode([
            'success' => true,
            'projects' => $projects
        ]);
    }

    // Speichert die Projekt-Reihenfolge in project_order.json
    public static function saveProjectOrder($clientDir) {
        self::requireClientAccess($clientDir);
        $clientDir = rawurldecode($clientDir);
        
        // JSON-Body lesen
        $input = file_get_contents('php://input');
        $data = json_decode($input, true);
        
        if (!isset($data['order']) || !is_array($data['order'])) {
            http_response_code(400);
            echo json_encode([
                'success' => false,
                'error' => [
                    'code' => 'INVALID_DATA',
                    'message' => 'Ungültige Daten. "order" Array erwartet.'
                ]
            ]);
            return;
        }
        
        $base = realpath(__DIR__ . self::getAreaPath() . '/' . $clientDir);
        if (!$base || !is_dir($base)) {
            http_response_code(404);
            echo json_encode([
                'success' => false,
                'error' => [
                    'code' => 'NOT_FOUND',
                    'message' => 'Kundenordner nicht gefunden.'
                ]
            ]);
            return;
        }
        
        $projectOrderFile = $base . '/project_order.json';
        
        // Validiere dass alle Projekte im order Array auch wirklich existieren
        $existingProjects = [];
        foreach (scandir($base) as $entry) {
            if ($entry === '.' || $entry === '..') continue;
            if (is_dir($base . '/' . $entry)) {
                $existingProjects[] = $entry;
            }
        }
        
        foreach ($data['order'] as $projectName) {
            if (!in_array($projectName, $existingProjects)) {
                http_response_code(400);
                echo json_encode([
                    'success' => false,
                    'error' => [
                        'code' => 'INVALID_PROJECT',
                        'message' => "Projekt '$projectName' existiert nicht."
                    ]
                ]);
                return;
            }
        }
        
        // Speichere die neue Reihenfolge
        $result = file_put_contents($projectOrderFile, json_encode($data['order'], JSON_PRETTY_PRINT));
        
        if ($result === false) {
            http_response_code(500);
            echo json_encode([
                'success' => false,
                'error' => [
                    'code' => 'WRITE_ERROR',
                    'message' => 'Fehler beim Speichern der project_order.json.'
                ]
            ]);
            return;
        }
        
        echo json_encode([
            'success' => true,
            'message' => 'Projekt-Reihenfolge gespeichert.'
        ]);
    }

    public static function adsFolders($clientDir, $projectName) {
        self::requireClientAccess($clientDir);
        $clientDir = rawurldecode($clientDir);
        $projectName = rawurldecode($projectName);
        $adsPath = __DIR__ . self::getAreaPath() . '/' . $clientDir . '/' . $projectName . '/ads';
        $folders = [];
        if (is_dir($adsPath)) {
            foreach (scandir($adsPath) as $entry) {
                if ($entry === '.' || $entry === '..') continue;
                if (is_dir($adsPath . '/' . $entry)) {
                    $folders[] = $entry;
                }
            }
        }
        header('Content-Type: application/json');
        echo json_encode(['folders' => $folders]);
    }

    public static function adsSubfolders($clientDir, $projectName, ...$adsFolders) {
        self::requireClientAccess($clientDir);
        $adsFolders = array_map('rawurldecode', $adsFolders);
        $base = __DIR__ . self::getAreaPath() . '/' . $clientDir . '/' . $projectName . '/ads';
        if (!empty($adsFolders)) {
            $base .= '/' . implode('/', $adsFolders);
        }
        $real = realpath($base);
        $folders = [];
        if ($real && is_dir($real) && strpos($real, realpath(__DIR__ . self::getAreaPath() . '/' . $clientDir)) === 0) {
            foreach (scandir($real) as $entry) {
                if ($entry === '.' || $entry === '..') continue;
                if (is_dir($real . '/' . $entry)) {
                    $folders[] = $entry;
                }
            }
        }
        header('Content-Type: application/json');
        echo json_encode(['folders' => $folders]);
    }

    // Liefert die Sub-Subfolder eines Subfolders in ads
    public static function adsSubSubfolders($clientDir, $projectName, $adsFolder, $subFolder) {
        self::requireClientAccess($clientDir);
        $adsFolder = rawurldecode($adsFolder);
        $subFolder = rawurldecode($subFolder);
        $base = __DIR__ . self::getAreaPath() . '/' . $clientDir . '/' . $projectName . '/ads/' . $adsFolder . '/' . $subFolder;
        $real = realpath($base);
        $folders = [];
        if ($real && is_dir($real) && strpos($real, realpath(__DIR__ . self::getAreaPath() . '/' . $clientDir)) === 0) {
            foreach (scandir($real) as $entry) {
                if ($entry === '.' || $entry === '..') continue;
                if (is_dir($real . '/' . $entry)) {
                    $folders[] = $entry;
                }
            }
        }
        header('Content-Type: application/json');
        echo json_encode(['folders' => $folders]);
    }

    // Liefert die Dateien eines beliebigen Unterordners in ads (beliebige Tiefe)
    public static function adsFolderFiles($clientDir, $projectName, ...$adsFolders) {
        self::requireClientAccess($clientDir);
        $adsFolders = array_map('rawurldecode', $adsFolders);
        $base = __DIR__ . self::getAreaPath() . '/' . $clientDir . '/' . $projectName . '/ads';
        if (!empty($adsFolders)) {
            $base .= '/' . implode('/', $adsFolders);
        }
        $real = realpath($base);
        $files = [];
        
        if ($real && is_dir($real) && strpos($real, realpath(__DIR__ . self::getAreaPath() . '/' . $clientDir)) === 0) {
            foreach (scandir($real) as $entry) {
                if ($entry === '.' || $entry === '..') continue;
                $full = $real . '/' . $entry;
                if (is_file($full)) {
                    // Ignoriere bestimmte Dateien
                    if (self::shouldIgnoreFile($entry)) {
                        continue;
                    }
                    
                    $ext = strtolower(pathinfo($entry, PATHINFO_EXTENSION));
                    $type = 'other';
                    if (in_array($ext, ['jpg','jpeg','png','gif','webp','svg'])) $type = 'image';
                    elseif (in_array($ext, ['mp4','mov','avi','webm'])) $type = 'video';
                    elseif (in_array($ext, ['html','htm'])) $type = 'html';
                    $urlParts = array_map('rawurlencode', $adsFolders);
                    $files[] = [
                        'name' => $entry,
                        'type' => $type,
                        'url' => "/area/$clientDir/$projectName/ads/" . implode('/', $urlParts) . (count($urlParts) ? '/' : '') . rawurlencode($entry)
                    ];
                }
            }
        }
        header('Content-Type: application/json');
        echo json_encode(['files' => $files]);
    }

    // Liefert sowohl Ordner als auch Dateien eines beliebigen Unterordners in ads (beliebige Tiefe)
    public static function adsFolderContent($clientDir, $projectName, ...$adsFolders) {
        self::requireClientAccess($clientDir);
        $adsFolders = array_map('rawurldecode', $adsFolders);
        $base = __DIR__ . self::getAreaPath() . '/' . $clientDir . '/' . $projectName . '/ads';
        if (!empty($adsFolders)) {
            $base .= '/' . implode('/', $adsFolders);
        }
        $real = realpath($base);
        $folders = [];
        $files = [];
        
        if ($real && is_dir($real) && strpos($real, realpath(__DIR__ . self::getAreaPath() . '/' . $clientDir)) === 0) {
            foreach (scandir($real) as $entry) {
                if ($entry === '.' || $entry === '..') continue;
                $full = $real . '/' . $entry;
                
                if (is_dir($full)) {
                    $folders[] = $entry;
                } elseif (is_file($full)) {
                    // Ignoriere bestimmte Dateien
                    if (self::shouldIgnoreFile($entry)) {
                        continue;
                    }
                    
                    $ext = strtolower(pathinfo($entry, PATHINFO_EXTENSION));
                    $type = 'other';
                    if (in_array($ext, ['jpg','jpeg','png','gif','webp','svg'])) $type = 'image';
                    elseif (in_array($ext, ['mp4','mov','avi','webm'])) $type = 'video';
                    elseif (in_array($ext, ['html','htm'])) $type = 'html';
                    $urlParts = array_map('rawurlencode', $adsFolders);
                    $files[] = [
                        'name' => $entry,
                        'type' => $type,
                        'url' => "/area/$clientDir/$projectName/ads/" . implode('/', $urlParts) . (count($urlParts) ? '/' : '') . rawurlencode($entry)
                    ];
                }
            }
        }
        
        header('Content-Type: application/json');
        echo json_encode([
            'folders' => $folders,
            'files' => $files
        ]);
    }

    // Liefert config.yaml aus einem beliebigen Ordnerpfad
    public static function getConfig($clientDir, $projectName, ...$adsFolders) {
        self::requireClientAccess($clientDir);
        $adsFolders = array_map('rawurldecode', $adsFolders);
        $base = __DIR__ . self::getAreaPath() . '/' . $clientDir . '/' . $projectName . '/ads';
        if (!empty($adsFolders)) {
            $base .= '/' . implode('/', $adsFolders);
        }
        $real = realpath($base);
        
        if ($real && is_dir($real) && strpos($real, realpath(__DIR__ . self::getAreaPath() . '/' . $clientDir)) === 0) {
            $configFile = $real . '/config.yaml';
            if (file_exists($configFile)) {
                header('Content-Type: text/plain; charset=utf-8');
                readfile($configFile);
                return;
            }
        }
        
        http_response_code(404);
        header('Content-Type: application/json');
        echo json_encode([
            'success' => false,
            'error' => [
                'code' => 'NOT_FOUND',
                'message' => 'config.yaml nicht gefunden'
            ]
        ]);
    }

    // Liefert die Details eines Projekts (inkl. Logo, Poster etc.)
    public static function projectDetails($clientDir, $projectName) {
        self::requireClientAccess($clientDir);
        $clientDir = rawurldecode($clientDir);
        $projectName = rawurldecode($projectName);

        $setupDir = __DIR__ . self::getAreaPath() . "/$clientDir/$projectName/setup";
        $poster = null;
        $logo = null;

        if (is_dir($setupDir)) {
            foreach (scandir($setupDir) as $file) {
                if ($file === '.' || $file === '..') continue;
                if (stripos($file, 'poster') === 0 && preg_match('/\.(jpg|jpeg|png|webp)$/i', $file)) {
                    $poster = '/area/' . rawurlencode($clientDir) . '/' . rawurlencode($projectName) . '/setup/' . rawurlencode($file);
                }
                if (!$logo && stripos($file, 'logo') === 0 && preg_match('/\.(jpg|jpeg|png|webp)$/i', $file)) {
                    $logo = '/area/' . rawurlencode($clientDir) . '/' . rawurlencode($projectName) . '/setup/' . rawurlencode($file);
                }
                if ($poster && $logo) break;
            }
        }

        header('Content-Type: application/json');
        echo json_encode([
            'success' => true,
            'data' => [
                'poster' => $poster,
                'logo' => $logo,
            ]
        ]);
    }

    // Rekursive Übersicht aller Ads als strukturierter Baum
    public static function adsOverview($clientDir, $projectName) {
        self::requireClientAccess($clientDir);
        $clientDir = rawurldecode($clientDir);
        $projectName = rawurldecode($projectName);
        $adsRoot = __DIR__ . self::getAreaPath() . "/$clientDir/$projectName/ads";
        $baseUrl = $_ENV['BACKEND_URL'] ?? (isset($_SERVER['HTTP_HOST']) ? (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] : '');

        // Hilfsfunktion für schöne Tab-Titel
        function beautifyTabName($name) {
            // Ersetze _ und - durch Leerzeichen, dann jedes Wort groß
            $name = str_replace(['_', '-'], ' ', $name);
            $name = mb_strtolower($name, 'UTF-8');
            $name = preg_replace_callback('/\b\w/u', function($m) { return mb_strtoupper($m[0], 'UTF-8'); }, $name);
            return $name;
        }

        function scanAdsFolder($absPath, $relPath, $baseUrl, $clientDir, $projectName) {
            $result = [];
            if (!is_dir($absPath)) return $result;
            $entries = array_diff(scandir($absPath), ['.', '..']);
            foreach ($entries as $entry) {
                $entryAbs = "$absPath/$entry";
                $entryRel = $relPath ? "$relPath/$entry" : $entry;
                if (is_dir($entryAbs)) {
                    $children = scanAdsFolder($entryAbs, $entryRel, $baseUrl, $clientDir, $projectName);
                    $depth = substr_count($entryRel, '/');
                    $type = match($depth) {
                        0 => 'category',
                        1 => 'subcategory',
                        2 => 'ad',
                        default => 'folder'
                    };
                    $node = [
                        'name' => $entry,
                        'title' => beautifyTabName($entry),
                        'type' => $type,
                    ];
                    if ($type === 'ad') {
                        // Prüfe, ob ein Unterordner mit 'html' im Namen existiert
                        $htmlFolder = null;
                        foreach (scandir($entryAbs) as $sub) {
                            if ($sub === '.' || $sub === '..') continue;
                            if (is_dir("$entryAbs/$sub") && stripos($sub, 'html') !== false) {
                                $htmlFolder = $sub;
                                break;
                            }
                        }
                        // Dateien sammeln, aber config.yaml (und config.yml) ignorieren
                        $node['files'] = array_values(array_filter($children, function($child) {
                            if (in_array($child['type'], ['category','subcategory','ad','folder'])) return false;
                            if (isset($child['name']) && preg_match('/^config\.ya?ml$/i', $child['name'])) return false;
                            return true;
                        }));
                        // Spezialfall: HTML-Ordner gefunden
                        if ($htmlFolder) {
                            $htmlAbs = "$entryAbs/$htmlFolder";
                            $htmlIndex = "$htmlAbs/index.html";
                            // Suche config.yaml eine Ebene über dem HTML-Ordner (im Ad-Ordner)
                            $configYaml = "$entryAbs/config.yaml";
                            $meta = [];
                            if (is_file($configYaml)) {
                                $yaml = file_get_contents($configYaml);
                                $width = 0;
                                $height = 0;
                                $lines = preg_split('/\r?\n/', $yaml);
                                foreach ($lines as $line) {
                                    if ($width === 0 && preg_match('/width\s*:\s*["\']?([\d,.]+)\s*([a-zA-Z%]*)["\']?/i', $line, $wMatch)) {
                                        $val = str_replace([',', ' '], '', $wMatch[1]);
                                        $width = (int)floatval($val);
                                    }
                                    if ($height === 0 && preg_match('/height\s*:\s*["\']?([\d,.]+)\s*([a-zA-Z%]*)["\']?/i', $line, $hMatch)) {
                                        $val = str_replace([',', ' '], '', $hMatch[1]);
                                        $height = (int)floatval($val);
                                    }
                                }
                                $meta['width'] = $width;
                                $meta['height'] = $height;
                            } else {
                                // Fallback: Versuche Dimensionen aus dem Ad-Ordnernamen zu extrahieren (z.B. 800x250)
                                $adFolderName = basename($entryAbs);
                                if (preg_match('/(\d{2,5})[xX](\d{2,5})/', $adFolderName, $matches)) {
                                    $meta['width'] = (int)$matches[1];
                                    $meta['height'] = (int)$matches[2];
                                    // file_put_contents(__DIR__.'/debug.log', "Fallback: Dimensionen aus Ordnername erkannt width={$meta['width']} height={$meta['height']} ($adFolderName)\n", FILE_APPEND);
                                }
                            }
                            if (is_file($htmlIndex)) {
                                $url = "$baseUrl/area/" . rawurlencode($clientDir) . "/" . rawurlencode($projectName) . "/ads/" . ($relPath ? str_replace('%2F', '/', rawurlencode($relPath)) . '/' : '') . rawurlencode($entry) . "/" . rawurlencode($htmlFolder) . "/index.html";
                                $fileMeta = [
                                    'name' => 'index.html',
                                    'type' => 'html',
                                    'url' => $url,
                                    'size' => is_file($htmlIndex) ? filesize($htmlIndex) : null,
                                    'width' => (isset($meta['width'])) ? $meta['width'] : 123,
                                    'height' => (isset($meta['height'])) ? $meta['height'] : 456
                                ];
                                // Füge index.html nur hinzu, wenn sie nicht schon in files ist
                                $already = false;
                                foreach ($node['files'] as $f) {
                                    if ($f['name'] === 'index.html') { $already = true; break; }
                                }
                                if (!$already) {
                                    $node['files'][] = $fileMeta;
                                }
                            }
                        }
                    } else {
                        $node['children'] = array_filter($children, fn($c) => in_array($c['type'], ['category','subcategory','ad','folder']));
                    }
                    $result[] = $node;
                } else {
                    // Datei
                    $ext = strtolower(pathinfo($entry, PATHINFO_EXTENSION));
                    $type = match(true) {
                        in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp']) => 'image',
                        in_array($ext, ['mp4', 'mov', 'webm']) => 'video',
                        $ext === 'html' => 'html',
                        default => 'file'
                    };
                    $url = "$baseUrl/area/" . rawurlencode($clientDir) . "/" . rawurlencode($projectName) . "/ads/" . ($relPath ? str_replace('%2F', '/', rawurlencode($relPath)) . '/' : '') . rawurlencode($entry);

                    $meta = [];
                    $fileAbs = $entryAbs;
                    // Dateigröße
                    $meta['size'] = is_file($fileAbs) ? filesize($fileAbs) : null;

                    // Dimensionen für Bilder
                    if ($type === 'image') {
                        $imgInfo = @getimagesize($fileAbs);
                        if ($imgInfo) {
                            $meta['width'] = $imgInfo[0];
                            $meta['height'] = $imgInfo[1];
                        }
                    }
                    // Dimensionen und Dauer für Videos (nur wenn ffprobe verfügbar)
                    if ($type === 'video' && is_file($fileAbs)) {
                        $ffprobePath = shell_exec('which ffprobe');
                        $ffprobeUsed = false;
                        if ($ffprobePath) {
                            $ffprobe = trim($ffprobePath);
                            if ($ffprobe !== '') {
                                $cmd = escapeshellcmd($ffprobe) . ' -v error -select_streams v:0 -show_entries stream=width,height,duration -of default=noprint_wrappers=1:nokey=1 ' . escapeshellarg($fileAbs);
                                $out = shell_exec($cmd);
                                if ($out) {
                                    $lines = explode("\n", trim($out));
                                    if (count($lines) >= 3) {
                                        $meta['width'] = (int)$lines[0];
                                        $meta['height'] = (int)$lines[1];
                                        $meta['duration'] = (float)$lines[2];
                                        $ffprobeUsed = true;
                                    }
                                }
                            }
                        }
                        // Fallback auf getID3, falls ffprobe nicht verfügbar oder fehlgeschlagen
                        if (!$ffprobeUsed) {
                            try {
                                $getID3 = new \getID3();
                                $info = $getID3->analyze($fileAbs);
                                if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) {
                                    $meta['width'] = (int)$info['video']['resolution_x'];
                                    $meta['height'] = (int)$info['video']['resolution_y'];
                                }
                                if (!empty($info['playtime_seconds'])) {
                                    $meta['duration'] = (float)$info['playtime_seconds'];
                                }
                            } catch (\Exception $e) {
                                // Fehler ignorieren, Metadaten bleiben leer
                            }
                        }
                    }

                    $result[] = array_merge([
                        'name' => $entry,
                        'title' => beautifyTabName($entry),
                        'type' => $type,
                        'url' => $url
                    ], $meta);
                }
            }
            usort($result, function($a, $b) {
                if (($a['type'] === 'file') !== ($b['type'] === 'file')) {
                    return $a['type'] === 'file' ? 1 : -1;
                }
                return strnatcasecmp($a['name'], $b['name']);
            });
            return $result;
        }

        $tree = scanAdsFolder($adsRoot, '', $baseUrl, $clientDir, $projectName);
        header('Content-Type: application/json');
        echo json_encode($tree, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
    }

    // Projekt→Client Mapping für Admin Smart URL Resolution
    public static function getProjectClientMapping() {
        // Nur Admins dürfen diese API verwenden
        $user = self::requireAuth();
        if (!isset($user['role']) || $user['role'] !== 'admin') {
            http_response_code(403);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false,
                'error' => [
                    'code' => 'FORBIDDEN',
                    'message' => 'Nur Administratoren können das Projekt-Client-Mapping abrufen.'
                ]
            ]);
            exit;
        }

        // Lade Admin-Daten für Client-Berechtigungen
        $adminData = self::getAdminData($user['username']);
        $disallowedClients = $adminData['disallowedClients'] ?? [];

        $areaPath = __DIR__ . self::getAreaPath();
        $mapping = [];

        // Durchsuche alle Client-Ordner
        if (is_dir($areaPath)) {
            $clientDirs = scandir($areaPath);
            foreach ($clientDirs as $clientDir) {
                if ($clientDir === '.' || $clientDir === '..' || !is_dir($areaPath . '/' . $clientDir)) {
                    continue;
                }

                // Prüfe ob Admin Zugriff auf diesen Client hat
                if (in_array($clientDir, $disallowedClients)) {
                    continue; // Client ist für diesen Admin nicht erlaubt
                }

                $clientPath = $areaPath . '/' . $clientDir;
                if (is_dir($clientPath)) {
                    $projects = scandir($clientPath);
                    foreach ($projects as $project) {
                        if ($project === '.' || $project === '..' || !is_dir($clientPath . '/' . $project)) {
                            continue;
                        }

                        // Ignoriere spezielle Dateien
                        if (in_array($project, ['project_order.json', 'logins.json'])) {
                            continue;
                        }

                        // Füge Projekt→Client Mapping hinzu
                        $mapping[$project] = $clientDir;
                    }
                }
            }
        }

        header('Content-Type: application/json');
        echo json_encode([
            'success' => true,
            'mapping' => $mapping,
            'adminInfo' => [
                'username' => $user['username'],
                'disallowedClients' => $disallowedClients
            ]
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }

    // Hilfsfunktion: Admin-Daten laden
    private static function getAdminData($username) {
        $adminFile = __DIR__ . '/../../storage/data/admins.json';
        if (file_exists($adminFile)) {
            $admins = json_decode(file_get_contents($adminFile), true);
            foreach ($admins as $admin) {
                if ($admin['username'] === $username) {
                    return $admin;
                }
            }
        }
        return [];
    }
}
