<?php
declare(strict_types=1);

namespace Celena\Module\AdminBase;

use Celena\Core\Http\Flash;
use Celena\Core\Http\Response;
use Celena\Core\Plugin\Hook;
use Celena\Core\Security\Auth;
use Celena\Core\Template\Engine;
use Celena\Module\Settings\Options;

/**
 * Базовый класс для всех контроллеров админки.
 * Инкапсулирует рендер в admin-теме с передачей общих переменных:
 * текущий пользователь, активный пункт меню, flash-сообщения, заголовок страницы.
 */
abstract class AdminController
{
    public function __construct(
        protected readonly Engine $engine,
        protected readonly Auth $auth,
        protected readonly Options $options,
    ) {
    }

    /** Отрендерить admin-шаблон и вернуть Response. */
    protected function view(string $template, array $vars = [], string $activeMenu = '', string $title = ''): Response
    {
        $user = $this->auth->user() ?? [];
        $this->engine->setTheme('admin');
        // Пункты меню плагинов с поддержкой submenu (children) и авто-подсветкой parent-а,
        // если активен любой из его дочерних слугов.
        $pluginMenu = Hook::filter('admin.sidebar', []);
        try {
            $pmManager = \Celena\Core\Kernel::container()->make(\Celena\Core\Plugin\PluginManager::class);
        } catch (\Throwable) {
            $pmManager = null;
        }
        foreach ($pluginMenu as &$pm) {
            $ownSlugs = (array) ($pm['active_slugs'] ?? [$pm['slug'] ?? '']);
            $pm['active'] = in_array($activeMenu, $ownSlugs, true);
            if (isset($pm['children']) && is_array($pm['children'])) {
                foreach ($pm['children'] as $ch) {
                    if (($ch['slug'] ?? '') === $activeMenu) {
                        $pm['active'] = true;
                    }
                }
            } else {
                $pm['children'] = [];
            }

            // Авто-добавление пункта «Документация» в children, если у плагина есть docs/.
            // Slug плагина определяем по полю slug или первому из active_slugs.
            $pluginSlug = (string) ($pm['slug'] ?? ($ownSlugs[0] ?? ''));
            if ($pluginSlug !== '' && $pmManager !== null) {
                try {
                    if ($pmManager->docsFor($pluginSlug) !== []) {
                        // Не дублируем, если плагин уже добавил «документация» вручную.
                        $hasDocs = false;
                        foreach ($pm['children'] as $ch) {
                            if (($ch['slug'] ?? '') === $pluginSlug . '-docs') { $hasDocs = true; break; }
                        }
                        if (!$hasDocs) {
                            $pm['children'][] = [
                                'url'        => '#',
                                'label'      => '📖 Документация',
                                'slug'       => $pluginSlug . '-docs',
                                'docs_url'   => '/admin/docs/plugin-panel?slug=' . rawurlencode($pluginSlug),
                                'docs_title' => (string) ($pm['label'] ?? $pluginSlug),
                            ];
                        }
                    }
                } catch (\Throwable) {
                }
            }
        }
        unset($pm);

        $notifications = Hook::filter('admin.notifications', []);
        // Стабильный id для каждого уведомления — по нему клиент помечает «прочитано»
        // (хранится в localStorage). Если поставщик не задал id — выводим из содержимого.
        foreach ($notifications as &$n) {
            if (!is_array($n)) {
                continue;
            }
            if (!isset($n['id']) || (string) $n['id'] === '') {
                $n['id'] = substr(md5(
                    (string) ($n['title'] ?? '') . '|' .
                    (string) ($n['url'] ?? '') . '|' .
                    (string) ($n['time'] ?? '')
                ), 0, 16);
            }
        }
        unset($n);
        $notificationsFingerprint = $notifications === [] ? '' : substr(md5(json_encode($notifications)), 0, 12);

        // Локали для переключателя языка в топбаре.
        $langs = [];
        $currentLocale = 'ru';
        $currentLangFlag = '🌐';
        $currentLangShort = 'RU';
        try {
            $container = \Celena\Core\Kernel::container();
            $locObj = $container->make(\Celena\Core\I18n\Locale::class);
            $trObj = $container->make(\Celena\Core\I18n\Translator::class);
            $currentLocale = $trObj->locale();
            $names = ['ru' => 'Русский', 'en' => 'English', 'uk' => 'Українська', 'de' => 'Deutsch',
                      'fr' => 'Français', 'es' => 'Español', 'pl' => 'Polski', 'kk' => 'Қазақша'];
            foreach ($locObj->available() as $code) {
                $short = strtoupper($code);
                $langs[] = [
                    'code'   => $code,
                    'name'   => $names[$code] ?? $short,
                    'short'  => $short,
                    'active' => $code === $currentLocale,
                ];
                if ($code === $currentLocale) {
                    $currentLangShort = $short;
                }
            }
        } catch (\Throwable $e) {
        }

        $merged = array_merge([
            'site' => [
                'name' => (string) $this->options->get('site_name', 'Celena CMS'),
                'url'  => (string) $this->options->get('site_url', ''),
            ],
            'user' => [
                'id'    => (int) ($user['id'] ?? 0),
                'name'  => (string) ($user['name'] ?? ''),
                'email' => (string) ($user['email'] ?? ''),
                'role'  => (string) ($user['role'] ?? ''),
            ],
            'page' => ['title' => $title],
            'active_menu' => $activeMenu,
            'flash' => Flash::pull(),
            'plugin_menu' => $pluginMenu,
            'notifications' => $notifications,
            'notifications_count' => count($notifications),
            'notifications_fingerprint' => $notificationsFingerprint,
            'langs' => $langs,
            'current_locale' => $currentLocale,
            'current_lang_flag' => $currentLangFlag,
            'current_lang_short' => $currentLangShort,
            'asset_v' => $this->assetVersion(),
        ], $vars);
        return Response::html($this->engine->render($template, $merged));
    }

    /**
     * Версия статики для cache-busting (?v=…). Берём максимальный mtime
     * ключевых css/js админки — при любой правке версия меняется, и браузер
     * подтягивает свежий файл без ручного хард-рефреша.
     */
    private function assetVersion(): string
    {
        $base = dirname(__DIR__, 2) . '/public/assets';
        $max = 0;
        foreach ([
            '/admin/admin.css', '/admin/admin.js',
            '/runtime/celena.css', '/runtime/celena.js',
            '/admin/celenaEditor.css', '/admin/celenaEditor.js',
        ] as $rel) {
            $t = @filemtime($base . $rel);
            if ($t !== false && $t > $max) {
                $max = $t;
            }
        }
        return $max > 0 ? (string) $max : '1';
    }

    protected function redirect(string $url, string $flash = '', string $type = 'success'): Response
    {
        if ($flash !== '') {
            Flash::add($type, $flash);
        }
        return Response::redirect($url);
    }

    /**
     * Гард по праву. Вернёт Response (редирект/403), если у текущего пользователя
     * нет разрешения $permission, иначе null. AuthMiddleware гарантирует лишь
     * «залогинен и не customer» — ролевую проверку каждый контроллер делает сам:
     *
     *   if ($deny = $this->deny('banners.manage')) return $deny;
     */
    protected function deny(string $permission, string $redirect = '/admin'): ?Response
    {
        if (\Celena\Core\Security\Permission::can($this->auth->user(), $permission)) {
            return null;
        }
        return $this->redirect($redirect, 'Доступ запрещён.', 'error');
    }
}
