<?php
 namespace App\Services; use App\Models\Setting; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\File; class ThemeManager { private const CACHE_KEY = 'theme_active'; private const CACHE_TTL = 3600; private const SETTING_KEY = 'active_theme'; private const DEFAULT_THEME = 'default'; private string $themesPath; public function __construct() { $this->themesPath = resource_path('themes'); } public function getActive(): string { return Cache::remember(self::CACHE_KEY, self::CACHE_TTL, function () { try { $theme = Setting::get(self::SETTING_KEY, self::DEFAULT_THEME); } catch (\Exception $e) { $theme = self::DEFAULT_THEME; } if ($this->exists($theme)) { return $theme; } if ($theme !== self::DEFAULT_THEME && $this->exists(self::DEFAULT_THEME)) { return self::DEFAULT_THEME; } return $this->getFirstAvailable() ?? self::DEFAULT_THEME; }); } private function getFirstAvailable(): ?string { if (!File::isDirectory($this->themesPath)) { return null; } foreach (File::directories($this->themesPath) as $path) { $name = basename($path); if ($this->exists($name)) { return $name; } } return null; } public function activate(string $theme): void { if (!$this->exists($theme)) { throw new \InvalidArgumentException("Theme '{$theme}' does not exist."); } Setting::set(self::SETTING_KEY, $theme); Cache::forget(self::CACHE_KEY); } public function exists(string $theme): bool { return File::isDirectory($this->getThemePath($theme)) && File::exists($this->getThemePath($theme) . '/theme.json'); } public function getThemePath(string $theme): string { return $this->themesPath . '/' . $theme; } public function getViewsPath(string $theme): string { return $this->getThemePath($theme) . '/views'; } public function getConfig(string $theme): array { $path = $this->getThemePath($theme) . '/theme.json'; if (!File::exists($path)) { return []; } return json_decode(File::get($path), true) ?? []; } public function getThemes(): Collection { if (!File::isDirectory($this->themesPath)) { return collect(); } return collect(File::directories($this->themesPath)) ->map(function ($path) { $name = basename($path); $config = $this->getConfig($name); if (empty($config)) { return null; } return array_merge($config, [ 'name' => $name, 'path' => $path, 'active' => $this->getActive() === $name, 'screenshot_url' => $this->getScreenshotUrl($name, $config), ]); }) ->filter() ->values(); } public function getAssets(?string $theme = null): array { $theme = $theme ?? $this->getActive(); $config = $this->getConfig($theme); $css = []; $js = []; foreach ($config['css'] ?? [] as $file) { $css[] = '/assets/themes/' . $theme . '/' . $file; } foreach ($config['js'] ?? [] as $file) { $js[] = '/assets/themes/' . $theme . '/' . $file; } return ['css' => $css, 'js' => $js]; } public function getParent(string $theme): ?string { $config = $this->getConfig($theme); return $config['parent'] ?? null; } private function getScreenshotUrl(string $theme, array $config): ?string { $screenshot = $config['screenshot'] ?? 'screenshot.png'; $path = $this->getThemePath($theme) . '/' . $screenshot; if (File::exists($path)) { return '/assets/themes/' . $theme . '/' . $screenshot; } return null; } } 