<?php
/**
 * Carga, validación y persistencia de settings.json con redundancia (settings.json.bak).
 */

require_once __DIR__ . '/youtube_url.php';

if (!function_exists('grid_decode_settings_json')) {
    function grid_decode_settings_json($raw) {
        if ($raw === false || $raw === null || $raw === '') {
            return null;
        }
        $data = json_decode($raw, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            return null;
        }
        if (!is_array($data)) {
            return null;
        }
        return $data;
    }
}

if (!function_exists('grid_os_widget_factory_order')) {
    /**
     * Orden de fábrica de los widgets del menú inicio (sidebar).
     *
     * @return list<string>
     */
    function grid_os_widget_factory_order(): array
    {
        return ['spotify', 'system', 'weather', 'notes', 'media'];
    }
}

if (!function_exists('grid_normalize_os_widget_order')) {
    /**
     * Normaliza el orden de widgets: claves válidas, sin duplicados, faltantes al final.
     * Migra el orden de fábrica anterior (Spotify al final) al actual (Spotify primero).
     *
     * @param mixed $order
     * @return list<string>
     */
    function grid_normalize_os_widget_order($order): array
    {
        $factory = grid_os_widget_factory_order();
        $valid = $factory;
        $seen = [];
        $out = [];
        foreach (is_array($order) ? $order : [] as $k) {
            $k = (string) $k;
            if (in_array($k, $valid, true) && !isset($seen[$k])) {
                $seen[$k] = true;
                $out[] = $k;
            }
        }
        foreach ($valid as $k) {
            if (!isset($seen[$k])) {
                $out[] = $k;
            }
        }
        if ($out === ['system', 'weather', 'notes', 'spotify']) {
            return $factory;
        }

        return $out;
    }
}

if (!function_exists('grid_merge_settings_defaults')) {
    function grid_merge_settings_defaults(array $defaults, array $partial) {
        $out = array_merge($defaults, $partial);
        if (!isset($out['categories']) || !is_array($out['categories']) || count($out['categories']) === 0) {
            $out['categories'] = ['General'];
        }
        if (!isset($out['apps']) || !is_array($out['apps'])) {
            $out['apps'] = [];
        }
        $arrayKeys = [
            'backgroundImages', 'backgroundVideos', 'youtubePlaylistPC', 'youtubePlaylistMobile',
            'recycleBin', 'pinnedApps', 'taskbarOpenOrder', 'fileExplorerTrash', 'fileExplorerConnections',
            'desktopShortcuts', 'osWidgetOrder',
        ];
        // launcherLayout es objeto anidado (order + folders); no forzar a array vacío.
        if (!isset($out['launcherLayout']) || !is_array($out['launcherLayout'])) {
            $out['launcherLayout'] = isset($defaults['launcherLayout']) && is_array($defaults['launcherLayout'])
                ? $defaults['launcherLayout']
                : null;
        }
        foreach ($arrayKeys as $k) {
            if (!isset($out[$k]) || !is_array($out[$k])) {
                $out[$k] = isset($defaults[$k]) && is_array($defaults[$k]) ? $defaults[$k] : [];
            }
        }
        $out['osWidgetOrder'] = grid_normalize_os_widget_order($out['osWidgetOrder'] ?? null);
        if (!isset($out['deploymentKind']) || !in_array($out['deploymentKind'], ['personal', 'company'], true)) {
            $out['deploymentKind'] = $defaults['deploymentKind'] ?? 'personal';
        }
        if (!isset($out['personalDisplayName']) || !is_string($out['personalDisplayName'])) {
            $out['personalDisplayName'] = '';
        }
        if (!isset($out['branches']) || !is_array($out['branches'])) {
            $out['branches'] = [];
        }
        if (!function_exists('antara_org_normalize_branches')) {
            require_once __DIR__ . '/org_structure_helpers.php';
        }
        $out['branches'] = antara_org_normalize_branches($out['branches']);
        if (!isset($out['defaultBranchId']) || !is_string($out['defaultBranchId'])) {
            $out['defaultBranchId'] = '';
        }
        if (!isset($out['branchAccessDefaults']) || !is_array($out['branchAccessDefaults'])) {
            $out['branchAccessDefaults'] = is_array($defaults['branchAccessDefaults'] ?? null)
                ? $defaults['branchAccessDefaults']
                : ['crossBranchAccessPolicy' => 'explicit_grant', 'userDirectoryScope' => 'branch'];
        }
        if (!isset($out['pwa']) || !is_array($out['pwa'])) {
            $out['pwa'] = is_array($defaults['pwa'] ?? null) ? $defaults['pwa'] : [];
        } else {
            $out['pwa'] = array_merge(is_array($defaults['pwa'] ?? null) ? $defaults['pwa'] : [], $out['pwa']);
        }
        return $out;
    }
}

if (!function_exists('antara_workspace_read_settings')) {
    /**
     * Lee settings.json con defaults (GRID_ROOT / ANTARA_ROOT).
     *
     * @return array<string, mixed>
     */
    function antara_workspace_read_settings(): array
    {
        if (!function_exists('grid_app_default_settings')) {
            require_once __DIR__ . '/grid_default_settings.php';
        }
        $def = grid_app_default_settings();
        $root = defined('GRID_ROOT') && is_string(GRID_ROOT) && GRID_ROOT !== ''
            ? GRID_ROOT
            : (defined('ANTARA_ROOT') && is_string(ANTARA_ROOT) ? ANTARA_ROOT : dirname(__DIR__));
        $sf = $root . DIRECTORY_SEPARATOR . 'settings.json';
        $bk = $root . DIRECTORY_SEPARATOR . 'settings.json.bak';
        return grid_read_settings_for_api($sf, $bk, $def);
    }
}

if (!function_exists('antara_settings_valid_branch_id')) {
    /** Comprueba si id existe en settings.branches (vacío = válido legado). */
    function antara_settings_valid_branch_id(array $settings, string $branchId): bool
    {
        if ($branchId === '') {
            return true;
        }
        foreach ($settings['branches'] ?? [] as $b) {
            if (is_array($b) && (string)($b['id'] ?? '') === $branchId) {
                return true;
            }
        }
        return false;
    }
}

if (!function_exists('grid_settings_structure_ok')) {
    /**
     * Estructura mínima para considerar el objeto recuperable sin depender solo de defaults vacíos.
     */
    function grid_settings_structure_ok(array $s) {
        return isset($s['categories']) && is_array($s['categories']) && count($s['categories']) > 0
            && isset($s['apps']) && is_array($s['apps']);
    }
}

if (!function_exists('grid_persist_settings')) {
    function grid_atomic_write_file($path, $contents) {
        $dir = dirname($path);
        if (!is_dir($dir)) {
            return false;
        }

        try {
            $rand = function_exists('random_bytes') ? bin2hex(random_bytes(4)) : (string)mt_rand(100000, 999999);
        } catch (\Throwable $e) {
            $rand = (string)mt_rand(100000, 999999);
        }

        $tmp = $path . '.tmp.' . getmypid() . '.' . $rand;
        $written = @file_put_contents($tmp, $contents, LOCK_EX);
        if ($written === false) {
            @unlink($tmp);
            return false;
        }

        // En Windows, rename() puede fallar si el destino existe: intentamos rotar a .old primero.
        $old = $path . '.old';
        if (file_exists($path)) {
            @unlink($old);
            if (!@rename($path, $old)) {
                // fallback best-effort
                @unlink($path);
            }
        }

        if (!@rename($tmp, $path)) {
            @unlink($tmp);
            // Si habíamos movido el original a .old, intentamos restaurarlo.
            if (file_exists($old) && !file_exists($path)) {
                @rename($old, $path);
            }
            return false;
        }

        @unlink($old);
        return true;
    }

    function grid_persist_settings($settingsFile, $backupFile, array $data) {
        $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
        if ($json === false) return false;

        // Validación fuerte antes de tocar disco: no escribir basura.
        $decoded = grid_decode_settings_json($json);
        if ($decoded === null) return false;
        $merged = grid_merge_settings_defaults([], $decoded);
        if (!grid_settings_structure_ok($merged)) return false;

        $hasBackup = is_string($backupFile) && $backupFile !== '';

        // 1) Si el primary actual es válido, lo copiamos a .bak ANTES de sobrescribir el primary.
        //    Esto evita que un guardado corrupto se replique también en el backup.
        if ($hasBackup && file_exists($settingsFile)) {
            $primaryRaw = @file_get_contents($settingsFile);
            $primaryDecoded = grid_decode_settings_json($primaryRaw);
            if ($primaryDecoded !== null) {
                $primaryMerged = grid_merge_settings_defaults([], $primaryDecoded);
                if (grid_settings_structure_ok($primaryMerged)) {
                    // Escribimos el backup como "último bueno" (best-effort).
                    grid_atomic_write_file($backupFile, $primaryRaw);
                }
            }
        }

        // 2) Escritura atómica del primary.
        $ok = grid_atomic_write_file($settingsFile, $json);
        if (!$ok) return false;

        // 3) Si no había backup aún, lo creamos (primera ejecución).
        if ($hasBackup && !file_exists($backupFile)) {
            grid_atomic_write_file($backupFile, $json);
        }

        return true;
    }
}

if (!function_exists('grid_settings_resolve_project_root')) {
    /**
     * Raíz del proyecto (donde está settings.json y media/).
     */
    function grid_settings_resolve_project_root($settingsFile) {
        if (defined('GRID_ROOT') && is_string(GRID_ROOT) && GRID_ROOT !== '') {
            return GRID_ROOT;
        }
        if (is_string($settingsFile) && $settingsFile !== '') {
            $rp = @realpath($settingsFile);
            if ($rp !== false && is_string($rp)) {
                return dirname($rp);
            }
        }
        $cwd = @realpath(getcwd() ?: '.');
        if ($cwd !== false && is_string($cwd)) {
            return $cwd;
        }
        return dirname(__DIR__);
    }
}

if (!function_exists('grid_settings_youtube_background_active')) {
    function grid_settings_youtube_background_active(array $s) {
        if (strtolower((string)($s['backgroundType'] ?? '')) === 'youtube') {
            return true;
        }
        if (trim((string)($s['youtubeBackground'] ?? '')) !== '') {
            return true;
        }
        if (trim((string)($s['youtubeBackgroundMobile'] ?? '')) !== '') {
            return true;
        }
        if (!empty($s['usePlaylist'])) {
            $pc = $s['youtubePlaylistPC'] ?? [];
            $mb = $s['youtubePlaylistMobile'] ?? [];
            if (is_array($pc) && count($pc) > 0) {
                return true;
            }
            if (is_array($mb) && count($mb) > 0) {
                return true;
            }
        }
        return false;
    }
}

if (!function_exists('grid_settings_local_background_slots_empty')) {
    /**
     * Fondos locales (archivos en media/) no configurados en JSON.
     */
    function grid_settings_local_background_slots_empty(array $s) {
        $url = trim((string)($s['backgroundUrl'] ?? ''));
        $imgs = $s['backgroundImages'] ?? [];
        $vids = $s['backgroundVideos'] ?? [];
        $noUrl = ($url === '');
        $noImgs = !is_array($imgs) || count($imgs) === 0;
        $noVids = !is_array($vids) || count($vids) === 0;
        return $noUrl && $noImgs && $noVids;
    }
}

if (!function_exists('grid_settings_collect_root_media_assets')) {
    /**
     * Archivos subidos a la raíz de media/ (no media/explorer/...), típicos de fondos del escritorio.
     *
     * @return array{images: list<string>, videos: list<string>} rutas relativas con barra /
     */
    function grid_settings_collect_root_media_assets($projectRoot) {
        $images = [];
        $videos = [];
        $root = rtrim((string)$projectRoot, "\\/\r\n");
        $dir = $root . DIRECTORY_SEPARATOR . 'media';
        if (!is_dir($dir)) {
            return ['images' => [], 'videos' => []];
        }
        $imgExt = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'avif'];
        $vidExt = ['mp4', 'webm', 'mov', 'ogg', 'm4v'];
        $dh = @opendir($dir);
        if ($dh === false) {
            return ['images' => [], 'videos' => []];
        }
        while (($name = readdir($dh)) !== false) {
            if ($name === '.' || $name === '..') {
                continue;
            }
            if ($name[0] === '.') {
                continue;
            }
            $full = $dir . DIRECTORY_SEPARATOR . $name;
            if (!is_file($full)) {
                continue;
            }
            $ext = strtolower((string)pathinfo($name, PATHINFO_EXTENSION));
            $rel = 'media/' . str_replace('\\', '/', $name);
            if (in_array($ext, $imgExt, true)) {
                $images[] = $rel;
            } elseif (in_array($ext, $vidExt, true)) {
                $videos[] = $rel;
            }
        }
        closedir($dh);
        sort($images, SORT_NATURAL | SORT_FLAG_CASE);
        sort($videos, SORT_NATURAL | SORT_FLAG_CASE);
        return ['images' => $images, 'videos' => $videos];
    }
}

if (!function_exists('grid_settings_detect_client_logo_rel')) {
    /**
     * Primera coincidencia logos_sinapsys/logo_cliente.<ext>
     */
    function grid_settings_detect_client_logo_rel($projectRoot) {
        $root = rtrim((string)$projectRoot, "\\/\r\n");
        $logoDir = $root . DIRECTORY_SEPARATOR . 'logos_sinapsys';
        if (!is_dir($logoDir)) {
            return '';
        }
        $g = glob($logoDir . DIRECTORY_SEPARATOR . 'logo_cliente.*') ?: [];
        foreach ($g as $p) {
            if (is_file($p)) {
                return 'logos_sinapsys/' . basename($p);
            }
        }
        return '';
    }
}

if (!function_exists('grid_settings_scan_disk_personalization')) {
    /**
     * Evidencia en disco (logo cliente + archivos sueltos en media/, no subcarpetas).
     *
     * @return array{clientLogoRel: string, rootImages: list<string>, rootVideos: list<string>}
     */
    function grid_settings_scan_disk_personalization($projectRoot) {
        $root = (is_string($projectRoot) && $projectRoot !== '')
            ? rtrim($projectRoot, "\\/\r\n")
            : grid_settings_resolve_project_root('settings.json');
        $assets = grid_settings_collect_root_media_assets($root);
        return [
            'clientLogoRel' => grid_settings_detect_client_logo_rel($root),
            'rootImages' => $assets['images'],
            'rootVideos' => $assets['videos'],
        ];
    }
}

if (!function_exists('grid_settings_values_equal')) {
    function grid_settings_values_equal($a, $b) {
        if ($a === $b) {
            return true;
        }
        if (is_array($a) && is_array($b)) {
            return json_encode($a) === json_encode($b);
        }
        return false;
    }
}

if (!function_exists('grid_settings_merge_scan_into_settings')) {
    /**
     * Aplica reglas de enriquecimiento a partir de un escaneo (sin leer disco de nuevo).
     *
     * @param array<string, mixed> $settings
     * @param array{clientLogoRel?: string, rootImages?: list<string>, rootVideos?: list<string>} $scan
     * @return array<string, mixed>
     */
    function grid_settings_merge_scan_into_settings(array $settings, array $scan) {
        $out = $settings;
        $logoRel = isset($scan['clientLogoRel']) ? trim((string)$scan['clientLogoRel']) : '';
        if ($logoRel !== '') {
            $cur = trim((string)($out['clientLogoRel'] ?? ''));
            if ($cur === '') {
                $out['clientLogoRel'] = $logoRel;
            }
        }

        if (grid_settings_youtube_background_active($out)) {
            return $out;
        }
        if (!grid_settings_local_background_slots_empty($out)) {
            return $out;
        }

        $imgs = isset($scan['rootImages']) && is_array($scan['rootImages']) ? $scan['rootImages'] : [];
        $vids = isset($scan['rootVideos']) && is_array($scan['rootVideos']) ? $scan['rootVideos'] : [];
        if (count($imgs) === 0 && count($vids) === 0) {
            return $out;
        }

        if (count($vids) > 0 && count($imgs) === 0) {
            $out['backgroundType'] = 'videos';
            $out['backgroundVideos'] = $vids;
            $out['backgroundUrl'] = '';
            $out['backgroundImages'] = [];
            return $out;
        }
        if (count($imgs) > 0 && count($vids) === 0) {
            if (count($imgs) === 1) {
                $out['backgroundType'] = 'file';
                $out['backgroundUrl'] = $imgs[0];
                $out['backgroundImages'] = [];
                $out['backgroundVideos'] = [];
                $out['slideshowEnabled'] = false;
            } else {
                $out['backgroundType'] = 'images';
                $out['backgroundImages'] = $imgs;
                $out['backgroundUrl'] = '';
                $out['backgroundVideos'] = [];
                $out['slideshowEnabled'] = true;
            }
            return $out;
        }
        if (count($vids) >= count($imgs)) {
            $out['backgroundType'] = 'videos';
            $out['backgroundVideos'] = $vids;
            $out['backgroundUrl'] = '';
            $out['backgroundImages'] = [];
        } else {
            $out['backgroundType'] = 'images';
            $out['backgroundImages'] = $imgs;
            $out['backgroundUrl'] = '';
            $out['backgroundVideos'] = [];
            $out['slideshowEnabled'] = count($imgs) > 1;
        }
        return $out;
    }
}

if (!function_exists('grid_settings_enrichment_diff')) {
    /**
     * Claves que puede modificar el enriquecimiento desde disco.
     *
     * @return list<array{key: string, from: mixed, to: mixed}>
     */
    function grid_settings_enrichment_diff(array $before, array $after) {
        $keys = ['clientLogoRel', 'clientLogoMediaFrame', 'backgroundType', 'backgroundUrl', 'backgroundImages', 'backgroundVideos', 'slideshowEnabled'];
        $out = [];
        foreach ($keys as $k) {
            $b = $before[$k] ?? null;
            $a = $after[$k] ?? null;
            if (!grid_settings_values_equal($b, $a)) {
                $out[] = ['key' => $k, 'from' => $b, 'to' => $a];
            }
        }
        return $out;
    }
}

if (!function_exists('grid_settings_apply_disk_enrichment')) {
    /**
     * Integra en $settings rutas detectadas en disco cuando el JSON no las refleja (p. ej. tras pérdida de settings o listas vacías).
     */
    function grid_settings_apply_disk_enrichment(array $settings, $projectRoot) {
        $root = (is_string($projectRoot) && $projectRoot !== '')
            ? rtrim($projectRoot, "\\/\r\n")
            : grid_settings_resolve_project_root('settings.json');
        $scan = grid_settings_scan_disk_personalization($root);
        return grid_settings_merge_scan_into_settings($settings, $scan);
    }
}

if (!function_exists('grid_load_settings_with_recovery')) {
    /**
     * Lee settings.json; si está dañado o incompleto, intenta settings.json.bak y fusiona con defaults.
     * Si hubo reparación o el fichero no existía, escribe el resultado en settings.json y la copia .bak.
     *
     * @param bool $recoveryAlert (por referencia) true si hubo archivos dañados y no se pudo leer ningún JSON válido (solo defaults).
     * @param bool $persistFailed (por referencia) true si hubo que guardar y la escritura falló (p. ej. permisos).
     * @return array settings finales
     */
    function grid_load_settings_with_recovery($settingsFile, $backupFile, array $defaultSettings, &$recoveryAlert, &$persistFailed) {
        $recoveryAlert = false;
        $persistFailed = false;

        $primaryRaw = file_exists($settingsFile) ? @file_get_contents($settingsFile) : false;
        $primary = grid_decode_settings_json($primaryRaw);
        $primaryMerged = $primary !== null ? grid_merge_settings_defaults($defaultSettings, $primary) : null;
        $primaryOk = $primaryMerged !== null && grid_settings_structure_ok($primaryMerged);

        $backupRaw = file_exists($backupFile) ? @file_get_contents($backupFile) : false;
        $backupDecoded = grid_decode_settings_json($backupRaw);
        $backupMerged = $backupDecoded !== null ? grid_merge_settings_defaults($defaultSettings, $backupDecoded) : null;
        $backupOk = $backupMerged !== null && grid_settings_structure_ok($backupMerged);

        $hadAnySettingsFile = file_exists($settingsFile) || file_exists($backupFile);
        if ($hadAnySettingsFile && $primary === null && $backupDecoded === null) {
            $recoveryAlert = true;
        }

        $repaired = false;

        if ($primaryOk) {
            $final = $primaryMerged;
        } elseif ($backupOk) {
            $final = $backupMerged;
            $repaired = true;
        } else {
            $final = $defaultSettings;
            if ($primaryMerged !== null) {
                $final = grid_merge_settings_defaults($defaultSettings, $primaryMerged);
            } elseif ($backupMerged !== null) {
                $final = grid_merge_settings_defaults($defaultSettings, $backupMerged);
            }
            $repaired = true;
        }

        if (!$primaryOk || $primary === null) {
            $repaired = true;
        }

        $projectRoot = grid_settings_resolve_project_root($settingsFile);
        $beforeDisk = $final;
        $final = grid_settings_apply_disk_enrichment($final, $projectRoot);
        $diskEnrichmentChanged = serialize($beforeDisk) !== serialize($final);

        if ($repaired || !file_exists($settingsFile) || $diskEnrichmentChanged) {
            $ok = grid_persist_settings($settingsFile, $backupFile, $final);
            if (!$ok) {
                $persistFailed = true;
            }
        } elseif ($primaryOk && !file_exists($backupFile)) {
            $ok = grid_persist_settings($settingsFile, $backupFile, $final);
            if (!$ok) {
                $persistFailed = true;
            }
        }

        return $final;
    }
}

if (!function_exists('grid_read_settings_for_api')) {
    /**
     * Lectura segura para merge en API (repara y persiste si el JSON principal está roto).
     */
    function grid_read_settings_for_api($settingsFile, $backupFile, array $defaultSettings) {
        $_a = false;
        $_p = false;
        return grid_load_settings_with_recovery($settingsFile, $backupFile, $defaultSettings, $_a, $_p);
    }
}
