<?php

/**
 * CodePress Admin Console - Entry Point
 * Access via: //admin/login|dashboard|content|config|plugins|users|logout
 */

// Security headers
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: SAMEORIGIN');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: strict-origin-when-cross-origin');
header_remove('X-Powered-By');

// Load admin components
$appConfig = require __DIR__ . '/../admin/config/app.php';
require_once __DIR__ . '/../admin/src/AdminAuth.php';

$auth = new AdminAuth($appConfig);

// Routing
$route = $_GET['route'] ?? '';

// Public routes (no auth required)
if ($route === 'login') {
    handleLogin($auth);
    exit;
}

// All other routes require authentication
if (!$auth->isAuthenticated()) {
    header('Location: /admin/login');
    exit;
}

// Authenticated routes
switch ($route) {
    case 'logout':
        $auth->logout();
        header('Location: /admin/login');
        exit;

    case 'dashboard':
    case '':
        handleDashboard($auth, $appConfig);
        break;

    case 'content':
        handleContent($auth, $appConfig);
        break;

    case 'content-edit':
        handleContentEdit($auth, $appConfig);
        break;

    case 'content-new':
        handleContentNew($auth, $appConfig);
        break;

    case 'content-delete':
        handleContentDelete($auth, $appConfig);
        break;

    case 'content-dir-create':
        handleContentDirCreate($auth, $appConfig);
        break;

    case 'content-dir-rename':
        handleContentDirRename($auth, $appConfig);
        break;

    case 'content-move':
        handleContentMove($auth, $appConfig);
        break;

    case 'content-dir-delete':
        handleContentDirDelete($auth, $appConfig);
        break;

    case 'config':
        handleConfig($auth, $appConfig);
        break;

    case 'theme':
        handleTheme($auth, $appConfig);
        break;

    case 'plugins':
        handlePlugins($auth, $appConfig);
        break;

    case 'plugins-config':
        handlePluginConfig($auth, $appConfig);
        break;

    case 'plugins-edit':
        handlePluginEdit($auth, $appConfig);
        break;

    case 'plugins-new':
        handlePluginNew($auth, $appConfig);
        break;

    case 'plugins-toggle':
        handlePluginToggle($auth, $appConfig);
        break;

    case 'plugins-delete':
        handlePluginDelete($auth, $appConfig);
        break;

    case 'media':
        handleMedia($auth, $appConfig);
        break;

    case 'media-list':
        handleMediaList($auth, $appConfig);
        break;

    case 'users':
        handleUsers($auth, $appConfig);
        break;

    default:
        header('Location: /admin/dashboard');
        exit;
}

// --- Route Handlers ---

function handleLogin(AdminAuth $auth): void
{
    $error = '';

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $csrfToken = $_POST['csrf_token'] ?? '';
        if (!$auth->verifyCsrf($csrfToken)) {
            $error = 'Ongeldige CSRF token. Probeer opnieuw.';
        } else {
            $username = trim($_POST['username'] ?? '');
            $password = $_POST['password'] ?? '';

            $result = $auth->login($username, $password);
            if ($result['success']) {
                $auth->regenerateCsrfToken();
                header('Location: /admin/dashboard');
                exit;
            }
            $error = $result['message'];
        }
    }

    $csrfToken = $auth->getCsrfToken();
    require __DIR__ . '/../admin/templates/login.php';
}

function handleDashboard(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();

    // Gather stats
    $contentDir = $config['content_dir'];
    $pluginsDir = $config['plugins_dir'];
    $configJson = $config['config_json'];

    $stats = [
        'pages' => countFiles($contentDir, ['md', 'php', 'html']),
        'directories' => countDirs($contentDir),
        'plugins' => countDirs($pluginsDir),
        'config_exists' => file_exists($configJson),
        'content_size' => formatSize(dirSize($contentDir)),
        'php_version' => PHP_VERSION,
    ];

    // Load site config
    $siteConfig = file_exists($configJson) ? json_decode(file_get_contents($configJson), true) : [];

    require __DIR__ . '/../admin/templates/layout.php';
}

function handleContent(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $contentDir = $config['content_dir'];
    $subdir = $_GET['dir'] ?? '';

    // Prevent path traversal
    $subdir = str_replace(['../', '..\\'], '', $subdir);
    $subdir = trim($subdir, '/');
    if ($subdir === '.' || $subdir === '') {
        $subdir = '';
    }
    $fullPath = rtrim($contentDir, '/') . '/' . $subdir;

    if (!is_dir($fullPath)) {
        $fullPath = $contentDir;
        $subdir = '';
    }

    $message = '';
    $messageType = '';

    // Handle file upload
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_FILES['file']['name'][0])) {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            $allowedExt = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'pdf', 'zip', 'mp4', 'webm', 'ogg', 'mp3', 'wav', 'doc', 'docx', 'xls', 'xlsx'];
            $uploaded = 0;
            $errors = [];
            foreach ($_FILES['file']['name'] as $i => $name) {
                if ($_FILES['file']['error'][$i] !== UPLOAD_ERR_OK) continue;
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                if (!in_array($ext, $allowedExt)) {
                    $errors[] = htmlspecialchars($name) . ' (niet toegestaan type)';
                    continue;
                }
                $filename = preg_replace('/[^a-zA-Z0-9._-]/', '_', $name);
                $dest = rtrim($fullPath, '/') . '/' . $filename;
                $n = 1;
                while (file_exists($dest)) {
                    $p = pathinfo($filename);
                    $dest = rtrim($fullPath, '/') . '/' . $p['filename'] . '_' . $n . '.' . ($p['extension'] ?? $ext);
                    $n++;
                }
                if (move_uploaded_file($_FILES['file']['tmp_name'][$i], $dest)) {
                    $uploaded++;
                } else {
                    $errors[] = htmlspecialchars($name);
                }
            }
            if ($uploaded > 0) {
                $message = $uploaded . ' bestand(en) geüpload.';
                $messageType = 'success';
            }
            if (!empty($errors)) {
                $message .= ' Fouten: ' . implode(', ', $errors);
                $messageType = $messageType ?: 'danger';
            }
        }
    }

    $items = scanContentDir($fullPath, $subdir);
    $route = 'content';

    require __DIR__ . '/../admin/templates/layout.php';
}

function handleContentEdit(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $contentDir = $config['content_dir'];
    $file = $_GET['file'] ?? '';
    $file = str_replace(['../', '..\\'], '', $file);
    $filePath = rtrim($contentDir, '/') . '/' . $file;
    $message = '';
    $messageType = '';

    // Validate path
    $realPath = realpath($filePath);
    $realContentDir = realpath($contentDir);
    if (!$realPath || !$realContentDir || strpos($realPath, $realContentDir) !== 0) {
        header('Location: /admin/content');
        exit;
    }

    $fileExt = pathinfo($filePath, PATHINFO_EXTENSION);
    $isEditable = in_array($fileExt, ['md', 'php', 'html']);

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            // Handle rename
            $newFilename = trim($_POST['filename'] ?? '');
            if (!empty($newFilename)) {
                $newFilename = preg_replace('/[^a-zA-Z0-9._-]/', '-', $newFilename);
                $newFilename .= '.' . $fileExt;
                $parentDir = dirname($filePath);
                $newFilePath = $parentDir . '/' . $newFilename;

                $realParentDir = realpath($parentDir);
                if ($realParentDir && strpos($realParentDir, $realContentDir) === 0) {
                    if ($newFilePath !== $filePath && !file_exists($newFilePath)) {
                        rename($filePath, $newFilePath);
                        $filePath = $newFilePath;
                        $newFile = dirname($file) . '/' . $newFilename;
                        $file = ltrim($newFile, './');
                    }
                }
            }

            // Save content only for editable files
            if ($isEditable) {
                $content = $_POST['content'] ?? '';
                $layout = $_POST['layout'] ?? '';
                if ($layout && in_array($layout, ['sidebar-content', 'content', 'sidebar', 'content-sidebar', 'content-sidebar-reverse'])) {
                    $content = updateContentFrontmatter($content, 'layout', $layout);
                }
                $plugins = isset($_POST['plugins']) && is_array($_POST['plugins'])
                    ? implode(', ', array_map('trim', $_POST['plugins']))
                    : '';
                $content = updateContentFrontmatter($content, 'plugins', $plugins);
                file_put_contents($filePath, $content);
            }

            $message = 'Bestand opgeslagen.';
            $messageType = 'success';
        }
    }

    if ($isEditable) {
        $fileContent = file_get_contents($filePath);
        $currentLayout = parseFrontmatterField($fileContent, 'layout', 'sidebar-content');
    } else {
        $fileContent = '';
        $currentLayout = 'sidebar-content';
    }

    $fileName = basename($filePath);
    $route = 'content-edit';

    // Get available plugins for per-page visibility
    $pluginsDir = $config['plugins_dir'];
    $availablePlugins = [];
    if (is_dir($pluginsDir)) {
        $items = scandir($pluginsDir);
        sort($items);
        foreach ($items as $item) {
            if ($item[0] === '.') continue;
            $pluginPath = $pluginsDir . '/' . $item;
            if (!is_dir($pluginPath) || !file_exists($pluginPath . '/' . $item . '.php')) continue;
            $hasConfig = file_exists($pluginPath . '/config.json');
            $pluginConfig = $hasConfig ? json_decode(file_get_contents($pluginPath . '/config.json'), true) : [];
            if (!($pluginConfig['viewable'] ?? true)) continue;
            $availablePlugins[] = $item;
        }
    }
    $currentPlugins = parseFrontmatterField($fileContent ?? '', 'plugins', '');
    $selectedPlugins = !empty($currentPlugins)
        ? array_map('trim', explode(',', $currentPlugins))
        : [];

    require __DIR__ . '/../admin/templates/layout.php';
}

function handleContentNew(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $contentDir = $config['content_dir'];
    $dir = $_GET['dir'] ?? '';
    $dir = str_replace(['../', '..\\'], '', $dir);
    $message = '';
    $messageType = '';

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            $filename = trim($_POST['filename'] ?? '');
            $content = $_POST['content'] ?? '';
            $type = $_POST['type'] ?? 'md';

            if (empty($filename)) {
                $message = 'Bestandsnaam is verplicht.';
                $messageType = 'danger';
            } else {
                // Sanitize filename
                $filename = preg_replace('/[^a-zA-Z0-9._-]/', '-', $filename);
                if (!preg_match('/\.(md|php|html)$/', $filename)) {
                    $filename .= '.' . $type;
                }
                $targetDir = rtrim($contentDir, '/') . '/' . $dir;
                $filePath = $targetDir . '/' . $filename;

                if (file_exists($filePath)) {
                    $message = 'Bestand bestaat al.';
                    $messageType = 'danger';
                } else {
                    if (!is_dir($targetDir)) {
                        mkdir($targetDir, 0755, true);
                    }

                    // Build frontmatter
                    $frontmatterLines = [];
                    $layout = $_POST['layout'] ?? '';
                    if ($layout && $layout !== 'sidebar-content' && in_array($layout, ['sidebar-content', 'content', 'sidebar', 'content-sidebar', 'content-sidebar-reverse'])) {
                        $frontmatterLines[] = 'layout: ' . $layout;
                    }
                    $plugins = isset($_POST['plugins']) && is_array($_POST['plugins'])
                        ? implode(', ', array_map('trim', $_POST['plugins']))
                        : '';
                    if (!empty($plugins)) {
                        $frontmatterLines[] = 'plugins: ' . $plugins;
                    }
                    if (!empty($frontmatterLines)) {
                        $content = "---\n" . implode("\n", $frontmatterLines) . "\n---\n" . $content;
                    }

                    file_put_contents($filePath, $content);
                    header('Location: /admin/content&dir=' . urlencode($dir));
                    exit;
                }
            }
        }
    }

    // Get available plugins for per-page visibility
    $pluginsDir = $config['plugins_dir'];
    $availablePlugins = [];
    if (is_dir($pluginsDir)) {
        $items = scandir($pluginsDir);
        sort($items);
        foreach ($items as $item) {
            if ($item[0] === '.') continue;
            $pluginPath = $pluginsDir . '/' . $item;
            if (!is_dir($pluginPath) || !file_exists($pluginPath . '/' . $item . '.php')) continue;
            $hasConfig = file_exists($pluginPath . '/config.json');
            $pluginConfig = $hasConfig ? json_decode(file_get_contents($pluginPath . '/config.json'), true) : [];
            if (!($pluginConfig['viewable'] ?? true)) continue;
            $availablePlugins[] = $item;
        }
    }

    $route = 'content-new';
    require __DIR__ . '/../admin/templates/layout.php';
}

function handleContentDelete(AdminAuth $auth, array $config): void
{
    $contentDir = $config['content_dir'];
    $file = $_GET['file'] ?? '';
    $file = str_replace(['../', '..\\'], '', $file);
    $filePath = rtrim($contentDir, '/') . '/' . $file;

    $realPath = realpath($filePath);
    $realContentDir = realpath($contentDir);

    if ($_SERVER['REQUEST_METHOD'] === 'POST'
        && $auth->verifyCsrf($_POST['csrf_token'] ?? '')
        && $realPath && $realContentDir
        && strpos($realPath, $realContentDir) === 0
    ) {
        if (is_file($filePath)) {
            unlink($filePath);
        }
    }

    $dir = dirname($file);
    header('Location: /admin/content&dir=' . urlencode($dir === '.' ? '' : $dir));
    exit;
}

function handleContentDirCreate(AdminAuth $auth, array $config): void
{
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: /admin/content');
        exit;
    }

    $contentDir = $config['content_dir'];
    $subdir = $_GET['dir'] ?? '';
    $subdir = str_replace(['../', '..\\'], '', $subdir);

    if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
        header('Location: /admin/content&dir=' . urlencode($subdir));
        exit;
    }

    $dirname = trim($_POST['dirname'] ?? '');
    if (!empty($dirname)) {
        $dirname = preg_replace('/[^a-zA-Z0-9._-]/', '-', $dirname);
        $targetDir = rtrim($contentDir, '/') . '/' . ($subdir ? $subdir . '/' . $dirname : $dirname);
        if (!file_exists($targetDir)) {
            mkdir($targetDir, 0755, true);
        }
    }

    header('Location: /admin/content&dir=' . urlencode($subdir));
    exit;
}

function handleContentDirRename(AdminAuth $auth, array $config): void
{
    $contentDir = $config['content_dir'];
    $dir = $_GET['dir'] ?? '';
    $dir = str_replace(['../', '..\\'], '', $dir);
    $fullPath = rtrim($contentDir, '/') . '/' . $dir;

    $realPath = realpath($fullPath);
    $realContentDir = realpath($contentDir);

    if (!$realPath || !$realContentDir || strpos($realPath, $realContentDir) !== 0 || !is_dir($fullPath)) {
        header('Location: /admin/content');
        exit;
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $parentDirPath = dirname($fullPath);

        if ($auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $newName = trim($_POST['new_name'] ?? '');
            if (!empty($newName)) {
                $newName = preg_replace('/[^a-zA-Z0-9._-]/', '-', $newName);
                $newPath = $parentDirPath . '/' . $newName;

                if (!file_exists($newPath)) {
                    rename($fullPath, $newPath);
                }
            }
        }

        $parentRelative = dirname($dir);
        header('Location: /admin/content&dir=' . urlencode($parentRelative === '.' ? '' : $parentRelative));
        exit;
    }

    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $route = 'content-dir-rename';
    require __DIR__ . '/../admin/templates/layout.php';
}

function handleContentMove(AdminAuth $auth, array $config): void
{
    $contentDir = $config['content_dir'];
    $item = $_GET['item'] ?? '';
    $item = str_replace(['../', '..\\'], '', $item);
    $item = ltrim($item, './');
    $fullPath = rtrim($contentDir, '/') . '/' . $item;

    $realPath = realpath($fullPath);
    $realContentDir = realpath($contentDir);

    if (!$realPath || !$realContentDir || strpos($realPath, $realContentDir) !== 0) {
        header('Location: /admin/content');
        exit;
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if ($auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $targetDir = trim($_POST['target_dir'] ?? '');
            $targetDir = str_replace(['../', '..\\'], '', $targetDir);
            $targetFullPath = rtrim($contentDir, '/') . '/' . $targetDir;
            $realTarget = realpath($targetFullPath);

            if ($realTarget && strpos($realTarget, $realContentDir) === 0 && is_dir($realTarget)) {
                $destPath = $realTarget . '/' . basename($fullPath);
                if (!file_exists($destPath)) {
                    rename($fullPath, $destPath);
                }
            }
        }

        $parentRelative = is_file($fullPath) ? dirname($item) : dirname($item);
        header('Location: /admin/content&dir=' . urlencode($parentRelative === '.' ? '' : $parentRelative));
        exit;
    }

    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();

    // Get all available content subdirectories for the move dropdown
    $dirs = getAllContentDirs($contentDir, $realContentDir);

    $route = 'content-move';
    require __DIR__ . '/../admin/templates/layout.php';
}

function handleContentDirDelete(AdminAuth $auth, array $config): void
{
    $contentDir = $config['content_dir'];
    $dir = $_GET['dir'] ?? '';
    $dir = str_replace(['../', '..\\'], '', $dir);
    $fullPath = rtrim($contentDir, '/') . '/' . $dir;

    $realPath = realpath($fullPath);
    $realContentDir = realpath($contentDir);

    if ($_SERVER['REQUEST_METHOD'] === 'POST'
        && $auth->verifyCsrf($_POST['csrf_token'] ?? '')
        && $realPath && $realContentDir
        && strpos($realPath, $realContentDir) === 0
        && is_dir($fullPath)
    ) {
        // Only delete empty directories
        $items = array_diff(scandir($fullPath), ['.', '..']);
        if (empty($items)) {
            rmdir($fullPath);
        }
    }

    $parentDir = dirname($dir);
    header('Location: /admin/content&dir=' . urlencode($parentDir === '.' ? '' : $parentDir));
    exit;
}

function handleConfig(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $configJson = $config['config_json'];
    $message = '';
    $messageType = '';

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            $jsonContent = $_POST['config_content'] ?? '';
            $parsed = json_decode($jsonContent, true);
            if ($parsed === null && json_last_error() !== JSON_ERROR_NONE) {
                $message = 'Ongeldige JSON: ' . json_last_error_msg();
                $messageType = 'danger';
            } else {
                file_put_contents($configJson, json_encode($parsed, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
                $message = 'Configuratie opgeslagen.';
                $messageType = 'success';
                $jsonContent = null; // reload from file
            }
        }
    }

    $siteConfig = file_exists($configJson) ? file_get_contents($configJson) : '{}';
    if (isset($jsonContent)) {
        $siteConfig = $jsonContent;
    }

    $route = 'config';
    require __DIR__ . '/../admin/templates/layout.php';
}

function handleTheme(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $configJson = $config['config_json'];
    $themesDir = $config['codepress_root'] . '/themes';
    $publicThemes = $config['codepress_root'] . '/public/themes';
    $message = '';
    $messageType = '';

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            $action = $_POST['action'] ?? '';

            if ($action === 'save') {
                $themeName = preg_replace('/[^a-zA-Z0-9_-]/', '', $_POST['theme'] ?? '');
                $themeFile = $themesDir . '/' . $themeName . '/theme.json';
                if (file_exists($themeFile)) {
                    $themeData = json_decode(file_get_contents($themeFile), true) ?? [];
                    foreach (['header_color', 'header_font_color', 'navigation_color', 'navigation_font_color', 'sidebar_background', 'sidebar_border'] as $field) {
                        $value = trim($_POST[$field] ?? '');
                        if (preg_match('/^#[0-9a-fA-F]{6}$/', $value)) {
                            $themeData[$field] = $value;
                        }
                    }
                    $themeData['name'] = trim($_POST['theme_name'] ?? $themeData['name'] ?? $themeName);
                    $themeData['header_height'] = preg_match('/^\d+$/', $_POST['header_height'] ?? '') ? trim($_POST['header_height']) : ($themeData['header_height'] ?? '56');
                    $themeData['nav_height'] = preg_match('/^\d+$/', $_POST['nav_height'] ?? '') ? trim($_POST['nav_height']) : ($themeData['nav_height'] ?? '42');
                    $themeData['background_image_opacity'] = preg_match('/^\d+$/', $_POST['background_image_opacity'] ?? '') ? max(0, min(100, intval($_POST['background_image_opacity']))) : ($themeData['background_image_opacity'] ?? '100');

                    // Handle background image upload
                    if (!empty($_FILES['bg_image']['name']) && $_FILES['bg_image']['error'] === UPLOAD_ERR_OK) {
                        $ext = strtolower(pathinfo($_FILES['bg_image']['name'], PATHINFO_EXTENSION));
                        if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'])) {
                            $imgName = $themeName . '_bg.' . $ext;
                            if (!is_dir($publicThemes)) mkdir($publicThemes, 0755, true);
                            move_uploaded_file($_FILES['bg_image']['tmp_name'], $publicThemes . '/' . $imgName);
                            $themeData['background_image'] = $imgName;
                        }
                    }
                    // Handle background image URL
                    $bgUrl = trim($_POST['background_image_url'] ?? '');
                    if (!empty($bgUrl)) {
                        $themeData['background_image'] = $bgUrl;
                    }
                    // Handle background image removal
                    if (!empty($_POST['bg_image_remove'])) {
                        if ($themeData['background_image'] ?? '') {
                            $oldFile = $publicThemes . '/' . $themeData['background_image'];
                            if (file_exists($oldFile)) unlink($oldFile);
                        }
                        $themeData['background_image'] = '';
                    }

                    file_put_contents($themeFile, json_encode($themeData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
                    $message = 'Thema opgeslagen.';
                    $messageType = 'success';
                }
            } elseif ($action === 'activate') {
                $themeName = preg_replace('/[^a-zA-Z0-9_-]/', '', $_POST['theme'] ?? '');
                if (file_exists($themesDir . '/' . $themeName . '/theme.json')) {
                    $cfg = json_decode(file_get_contents($configJson), true) ?? [];
                    $cfg['active_theme'] = $themeName;
                    file_put_contents($configJson, json_encode($cfg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
                    $message = 'Thema geactiveerd.';
                    $messageType = 'success';
                }
            } elseif ($action === 'create') {
                $newName = preg_replace('/[^a-zA-Z0-9_-]/', '', $_POST['new_name'] ?? '');
                if (empty($newName)) {
                    $message = 'Geef een naam voor het nieuwe thema.';
                    $messageType = 'danger';
                } elseif (file_exists($themesDir . '/' . $newName . '/theme.json')) {
                    $message = 'Thema bestaat al.';
                    $messageType = 'danger';
                } else {
                    $defaults = [
                        'name' => $newName,
                        'header_color' => '#0a369d',
                        'header_font_color' => '#ffffff',
                        'header_height' => '56',
                        'navigation_color' => '#2754b4',
                        'navigation_font_color' => '#ffffff',
                        'nav_height' => '42',
                        'sidebar_background' => '#f8f9fa',
                        'sidebar_border' => '#dee2e6',
                        'background_image' => '',
                        'background_image_opacity' => '100',
                    ];
                    $newThemeDir = $themesDir . '/' . $newName;
                    mkdir($newThemeDir, 0755, true);
                    file_put_contents($newThemeDir . '/theme.json', json_encode($defaults, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
                    $message = 'Thema aangemaakt.';
                    $messageType = 'success';
                }
            } elseif ($action === 'delete') {
                $themeName = preg_replace('/[^a-zA-Z0-9_-]/', '', $_POST['theme'] ?? '');
                $themeDir = $themesDir . '/' . $themeName;
                if (is_dir($themeDir) && $themeName !== 'default') {
                    $themeFile = $themeDir . '/theme.json';
                    if (file_exists($themeFile)) unlink($themeFile);
                    // Remove theme directory if empty
                    $remaining = array_diff(scandir($themeDir), ['.', '..']);
                    if (empty($remaining)) rmdir($themeDir);
                    $cfg = json_decode(file_get_contents($configJson), true) ?? [];
                    if (($cfg['active_theme'] ?? '') === $themeName) {
                        $cfg['active_theme'] = 'default';
                        file_put_contents($configJson, json_encode($cfg, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
                    }
                    $message = 'Thema verwijderd.';
                    $messageType = 'success';
                }
            }
        }
    }

    // Load active theme name
    $configData = json_decode(file_get_contents($configJson), true) ?? [];
    $activeTheme = $configData['active_theme'] ?? 'default';

    // Load all themes
    $themes = [];
    if (is_dir($themesDir)) {
        foreach (scandir($themesDir) as $item) {
            if ($item[0] === '.') continue;
            $themeJson = $themesDir . '/' . $item . '/theme.json';
            if (is_dir($themesDir . '/' . $item) && file_exists($themeJson)) {
                $themeData = json_decode(file_get_contents($themeJson), true) ?? [];
                $themes[$item] = $themeData;
            }
        }
    }

    // Load theme being edited
    $editThemeName = preg_replace('/[^a-zA-Z0-9_-]/', '', $_GET['edit'] ?? '');
    $editTheme = null;
    if ($editThemeName && isset($themes[$editThemeName])) {
        $editTheme = $themes[$editThemeName];
        $editTheme['name'] = $editThemeName;
    }

    $route = 'theme';
    require __DIR__ . '/../admin/templates/layout.php';
}

function handlePlugins(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $pluginsDir = $config['plugins_dir'];
    $plugins = [];

    if (is_dir($pluginsDir)) {
        foreach (scandir($pluginsDir) as $item) {
            if ($item[0] === '.') continue;
            $pluginPath = $pluginsDir . '/' . $item;
            if (!is_dir($pluginPath)) continue;

            $hasConfig = file_exists($pluginPath . '/config.json');
            $pluginConfig = $hasConfig ? json_decode(file_get_contents($pluginPath . '/config.json'), true) : [];
            $hasMainFile = file_exists($pluginPath . '/' . $item . '.php');
            $hasReadme = file_exists($pluginPath . '/README.md');

            $plugins[] = [
                'name' => $item,
                'path' => $pluginPath,
                'viewable' => $pluginConfig['viewable'] ?? true,
                'config' => $pluginConfig,
                'has_config' => $hasConfig,
                'has_main' => $hasMainFile,
                'has_readme' => $hasReadme,
            ];
        }
    }

    $route = 'plugins';
    require __DIR__ . '/../admin/templates/layout.php';
}

function handlePluginConfig(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $pluginsDir = $config['plugins_dir'];
    $pluginName = $_GET['plugin'] ?? '';
    $pluginName = preg_replace('/[^a-zA-Z0-9_-]/', '', $pluginName);
    $pluginPath = $pluginsDir . '/' . $pluginName;
    $configFile = $pluginPath . '/config.json';
    $message = '';
    $messageType = '';

    if (empty($pluginName) || !is_dir($pluginPath)) {
        header('Location: /admin/plugins');
        exit;
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            $postConfig = $_POST['config'] ?? [];
            $currentConfig = file_exists($configFile) ? json_decode(file_get_contents($configFile), true) : [];

            $newConfig = array_replace_recursive($currentConfig, $postConfig);

            // Convert checkbox values: unchecked checkboxes are not sent
            foreach (array_keys($currentConfig) as $key) {
                if (is_bool($currentConfig[$key])) {
                    $newConfig[$key] = isset($postConfig[$key]) && $postConfig[$key] === '1';
                }
            }

            file_put_contents($configFile, json_encode($newConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
            $message = 'Configuratie opgeslagen voor plugin: ' . htmlspecialchars($pluginName);
            $messageType = 'success';
        }
    }

    $pluginConfig = file_exists($configFile) ? json_decode(file_get_contents($configFile), true) : [];
    $route = 'plugins-config';

    require __DIR__ . '/../admin/templates/layout.php';
}

function handlePluginEdit(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $pluginsDir = $config['plugins_dir'];
    $pluginName = preg_replace('/[^a-zA-Z0-9_-]/', '', $_GET['plugin'] ?? '');
    $pluginPath = $pluginsDir . '/' . $pluginName;
    $pluginFile = $pluginPath . '/' . $pluginName . '.php';
    $message = '';
    $messageType = '';

    if (empty($pluginName) || !is_dir($pluginPath) || !file_exists($pluginFile)) {
        header('Location: /admin/plugins');
        exit;
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            $content = $_POST['content'] ?? '';
            file_put_contents($pluginFile, $content);
            $message = 'Plugin opgeslagen.';
            $messageType = 'success';

            // Clear opcache if available
            if (function_exists('opcache_invalidate')) {
                opcache_invalidate($pluginFile, true);
            }
        }
    }

    $fileContent = file_get_contents($pluginFile);
    $route = 'plugins-edit';

    require __DIR__ . '/../admin/templates/layout.php';
}

function handlePluginNew(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $pluginsDir = $config['plugins_dir'];
    $message = '';
    $messageType = '';

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            $pluginName = trim($_POST['plugin_name'] ?? '');
            $pluginDesc = trim($_POST['plugin_desc'] ?? '');

            if (empty($pluginName)) {
                $message = 'Plugin naam is verplicht.';
                $messageType = 'danger';
            } elseif (!preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $pluginName)) {
                $message = 'Ongeldige plugin naam. Gebruik alleen letters, cijfers en underscores. Begin met een letter.';
                $messageType = 'danger';
            } else {
                $pluginPath = $pluginsDir . '/' . $pluginName;

                if (is_dir($pluginPath)) {
                    $message = 'Plugin met deze naam bestaat al.';
                    $messageType = 'danger';
                } else {
                    mkdir($pluginPath, 0755, true);

                    $pluginFile = $pluginPath . '/' . $pluginName . '.php';
                    $boilerplate = "<?php\n\nclass $pluginName\n{\n";
                    $boilerplate .= "    private ?CMSAPI \$api = null;\n    private array \$config;\n\n";
                    $boilerplate .= "    public function __construct()\n    {\n        \$this->config = [\n            'viewable' => true,\n        ];\n    }\n\n";
                    $boilerplate .= "    public function setAPI(CMSAPI \$api): void\n    {\n        \$this->api = \$api;\n    }\n\n";
                    $boilerplate .= "    public function getSidebarContent(): string\n    {\n        return '';\n    }\n\n";
                    $boilerplate .= "    public function getConfig(): array\n    {\n        return \$this->config;\n    }\n\n";
                    $boilerplate .= "    public function setConfig(array \$config): void\n    {\n        \$this->config = array_merge(\$this->config, \$config);\n    }\n}";
                    file_put_contents($pluginFile, $boilerplate);

                    $configFile = $pluginPath . '/config.json';
                    file_put_contents($configFile, json_encode(['viewable' => true], JSON_PRETTY_PRINT));

                    if ($pluginDesc) {
                        $readme = "# $pluginName\n\n$pluginDesc\n";
                        file_put_contents($pluginPath . '/README.md', $readme);
                    }

                    header('Location: /admin/plugins');
                    exit;
                }
            }
        }
    }

    $route = 'plugins-new';
    require __DIR__ . '/../admin/templates/layout.php';
}

function handlePluginToggle(AdminAuth $auth, array $config): void
{
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: /admin/plugins');
        exit;
    }

    $pluginsDir = $config['plugins_dir'];
    $pluginName = preg_replace('/[^a-zA-Z0-9_-]/', '', $_GET['plugin'] ?? '');
    $pluginPath = $pluginsDir . '/' . $pluginName;

    if (empty($pluginName) || !is_dir($pluginPath)) {
        header('Location: /admin/plugins');
        exit;
    }

    if ($auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
        $configFile = $pluginPath . '/config.json';
        if (file_exists($configFile)) {
            $pluginConfig = json_decode(file_get_contents($configFile), true);
            $pluginConfig['viewable'] = !($pluginConfig['viewable'] ?? true);
            file_put_contents($configFile, json_encode($pluginConfig, JSON_PRETTY_PRINT));
        } else {
            file_put_contents($configFile, json_encode(['viewable' => false], JSON_PRETTY_PRINT));
        }
    }

    header('Location: /admin/plugins');
    exit;
}

function handlePluginDelete(AdminAuth $auth, array $config): void
{
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: /admin/plugins');
        exit;
    }

    $pluginsDir = $config['plugins_dir'];
    $pluginName = preg_replace('/[^a-zA-Z0-9_-]/', '', $_GET['plugin'] ?? '');
    $pluginPath = $pluginsDir . '/' . $pluginName;

    if (empty($pluginName) || !is_dir($pluginPath)) {
        header('Location: /admin/plugins');
        exit;
    }

    if ($auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
        $di = new RecursiveDirectoryIterator($pluginPath, RecursiveDirectoryIterator::SKIP_DOTS);
        $files = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ($files as $file) {
            $file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath());
        }
        rmdir($pluginPath);
    }

    header('Location: /admin/plugins');
    exit;
}

function handleMedia(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $assetsDir = $config['assets_dir'];
    $message = '';
    $messageType = '';

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            if (!empty($_FILES['file']['name'][0]) && is_array($_FILES['file']['name'])) {
                if (!is_dir($assetsDir)) {
                    mkdir($assetsDir, 0755, true);
                }
                $allowedExt = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'pdf', 'zip', 'mp4', 'webm', 'ogg', 'mp3', 'wav', 'doc', 'docx', 'xls', 'xlsx'];
                $uploaded = 0;
                $errors = [];
                foreach ($_FILES['file']['name'] as $i => $name) {
                    if ($_FILES['file']['error'][$i] !== UPLOAD_ERR_OK) continue;
                    $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                    if (!in_array($ext, $allowedExt)) {
                        $errors[] = htmlspecialchars($name) . ' (niet toegestaan type)';
                        continue;
                    }
                    $filename = preg_replace('/[^a-zA-Z0-9._-]/', '_', $name);
                    $dest = $assetsDir . '/' . $filename;
                    $n = 1;
                    while (file_exists($dest)) {
                        $p = pathinfo($filename);
                        $dest = $assetsDir . '/' . $p['filename'] . '_' . $n . '.' . ($p['extension'] ?? $ext);
                        $n++;
                    }
                    if (move_uploaded_file($_FILES['file']['tmp_name'][$i], $dest)) {
                        $uploaded++;
                    } else {
                        $errors[] = htmlspecialchars($name);
                    }
                }
                if ($uploaded > 0) {
                    $message = $uploaded . ' bestand(en) geüpload.';
                    $messageType = 'success';
                }
                if (!empty($errors)) {
                    $message .= ' Fouten: ' . implode(', ', $errors);
                    $messageType = $messageType ?: 'danger';
                }
            }

            if (!empty($_POST['delete'])) {
                $file = basename($_POST['delete']);
                $path = $assetsDir . '/' . $file;
                if (file_exists($path)) {
                    unlink($path);
                    $message = 'Bestand verwijderd.';
                    $messageType = 'success';
                }
            }
        }
    }

    // List files
    $files = [];
    if (is_dir($assetsDir)) {
        foreach (scandir($assetsDir) as $item) {
            if ($item[0] === '.' || is_dir($assetsDir . '/' . $item)) continue;
            $ext = strtolower(pathinfo($item, PATHINFO_EXTENSION));
            $isImage = in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']);
            $files[] = [
                'name' => $item,
                'url' => '/-assets/' . $item,
                'size' => filesize($assetsDir . '/' . $item),
                'is_image' => $isImage,
                'ext' => $ext,
                'modified' => date('Y-m-d H:i', filemtime($assetsDir . '/' . $item)),
            ];
        }
    }
    // Sort newest first
    usort($files, fn($a, $b) => strcmp($b['modified'], $a['modified']));

    $route = 'media';
    require __DIR__ . '/../admin/templates/layout.php';
}

function handleMediaList(AdminAuth $auth, array $config): void
{
    $contentDir = realpath(__DIR__ . '/../content');
    $files = [];

    if ($contentDir === false || !is_dir($contentDir)) {
        header('Content-Type: application/json');
        echo json_encode([]);
        exit;
    }

    $mediaExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'mp3', 'wav', 'ogg', 'mp4', 'webm'];

    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($contentDir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($iterator as $path => $info) {
        if ($info->isDir()) continue;

        $rel = substr($path, strlen($contentDir) + 1);
        if (strpos($rel, '.') === 0) continue;

        $ext = strtolower($info->getExtension());
        if (!in_array($ext, $mediaExts)) continue;

        $isImage = in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']);
        $isAudio = in_array($ext, ['mp3', 'wav', 'ogg']);
        $isVideo = in_array($ext, ['mp4', 'webm']);

        $files[] = [
            'name' => $info->getFilename(),
            'url' => '/-media/' . $rel,
            'ext' => $ext,
            'is_image' => $isImage,
            'is_audio' => $isAudio,
            'is_video' => $isVideo,
        ];
    }

    usort($files, fn($a, $b) => strcasecmp($a['name'], $b['name']));

    header('Content-Type: application/json');
    echo json_encode($files);
    exit;
}

function handleUsers(AdminAuth $auth, array $config): void
{
    $user = $auth->getCurrentUser();
    $csrf = $auth->getCsrfToken();
    $message = '';
    $messageType = '';

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
            $message = 'Ongeldige CSRF token.';
            $messageType = 'danger';
        } else {
            $action = $_POST['action'] ?? '';

            if ($action === 'add') {
                $result = $auth->addUser(
                    trim($_POST['username'] ?? ''),
                    $_POST['password'] ?? '',
                    $_POST['role'] ?? 'admin'
                );
                $message = $result['message'];
                $messageType = $result['success'] ? 'success' : 'danger';
            } elseif ($action === 'delete') {
                $result = $auth->deleteUser($_POST['delete_username'] ?? '');
                $message = $result['message'];
                $messageType = $result['success'] ? 'success' : 'danger';
            } elseif ($action === 'change_password') {
                $result = $auth->changePassword(
                    $_POST['pw_username'] ?? '',
                    $_POST['new_password'] ?? ''
                );
                $message = $result['message'];
                $messageType = $result['success'] ? 'success' : 'danger';
            }
        }
    }

    $users = $auth->getUsers();
    $route = 'users';
    require __DIR__ . '/../admin/templates/layout.php';
}

// --- Frontmatter helpers ---

function parseFrontmatterField(string $content, string $key, string $default = ''): string
{
    if (preg_match('/^---\s*\n(.*?)\n---\s*\n/s', $content, $matches)) {
        $metaContent = $matches[1];
        foreach (explode("\n", $metaContent) as $line) {
            if (strpos($line, $key . ':') === 0) {
                [, $value] = explode(':', $line, 2);
                return trim($value, " \t'\"");
            }
        }
    }
    return $default;
}

function updateContentFrontmatter(string $content, string $key, string $value): string
{
    if (preg_match('/^(---\s*\n)(.*?)(\n---\s*\n.*)$/s', $content, $matches)) {
        $metaContent = $matches[2];
        $body = $matches[3];
        $found = false;
        $lines = explode("\n", $metaContent);
        foreach ($lines as $i => $line) {
            if (strpos($line, $key . ':') === 0) {
                $lines[$i] = $key . ': ' . $value;
                $found = true;
                break;
            }
        }
        if (!$found) {
            $lines[] = $key . ': ' . $value;
        }
        return $matches[1] . implode("\n", $lines) . $body;
    } else {
        return "---\n" . $key . ': ' . $value . "\n---\n" . $content;
    }
}

// --- Helper functions ---

function countFiles(string $dir, array $extensions): int
{
    $count = 0;
    if (!is_dir($dir)) return 0;
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS));
    foreach ($iterator as $file) {
        if ($file->isFile() && in_array($file->getExtension(), $extensions)) {
            $count++;
        }
    }
    return $count;
}

function countDirs(string $dir): int
{
    if (!is_dir($dir)) return 0;
    $count = 0;
    foreach (scandir($dir) as $item) {
        if ($item[0] !== '.' && is_dir($dir . '/' . $item)) $count++;
    }
    return $count;
}

function dirSize(string $dir): int
{
    $size = 0;
    if (!is_dir($dir)) return 0;
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS));
    foreach ($iterator as $file) {
        if ($file->isFile()) $size += $file->getSize();
    }
    return $size;
}

function formatSize(int $bytes): string
{
    $units = ['B', 'KB', 'MB', 'GB'];
    $i = 0;
    while ($bytes >= 1024 && $i < count($units) - 1) {
        $bytes /= 1024;
        $i++;
    }
    return round($bytes, 1) . ' ' . $units[$i];
}

function scanContentDir(string $fullPath, string $subdir): array
{
    $items = [];
    if (!is_dir($fullPath)) return $items;

    foreach (scandir($fullPath) as $item) {
        if ($item[0] === '.') continue;
        // Skip assets directory in content listing
        if ($item === 'assets' && is_dir($itemPath)) continue;
        $itemPath = $fullPath . '/' . $item;
        $relativePath = $subdir ? $subdir . '/' . $item : $item;

        $items[] = [
            'name' => $item,
            'path' => $relativePath,
            'is_dir' => is_dir($itemPath),
            'size' => is_file($itemPath) ? formatSize(filesize($itemPath)) : '',
            'modified' => date('d-m-Y H:i', filemtime($itemPath)),
            'extension' => is_file($itemPath) ? pathinfo($item, PATHINFO_EXTENSION) : '',
        ];
    }

    // Sort: directories first, then files alphabetically
    usort($items, function ($a, $b) {
        if ($a['is_dir'] !== $b['is_dir']) return $b['is_dir'] - $a['is_dir'];
        return strcasecmp($a['name'], $b['name']);
    });

    return $items;
}

function getAllContentDirs(string $contentDir, string $realContentDir): array
{
    $dirs = [];
    if (!is_dir($contentDir)) return $dirs;

    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($contentDir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($iterator as $path => $fileInfo) {
        if (!$fileInfo->isDir()) continue;
        $realPath = $fileInfo->getRealPath();
        if (strpos($realPath, $realContentDir) !== 0) continue;
        $relative = substr($realPath, strlen($realContentDir) + 1);
        if ($relative === false || $relative === '') continue;
        // Skip hidden directories (starting with -)
        if (strpos(basename($relative), '-') === 0) continue;
        $dirs[] = $relative;
    }

    sort($dirs);
    return $dirs;
}
