<?php

namespace Mtc\Plugins\BlockManager\Http\Controllers;

use Illuminate\Http\Response;
use Illuminate\Routing\Controller;
use Mtc\Plugins\BlockManager\Services\ViteAssetResolver;

/**
 * Renders the BlockManager admin SPA inside the legacy admin chrome
 * (top bar, mtc. only nav, etc.) when running in a real admin request, and
 * falls back to a self-contained standalone page when the legacy bootstrap
 * isn't available (per-test container; CLI). Either way the same Vue mount
 * div is emitted — the Vue bundle stays oblivious to the wrapper.
 */
class AdminController extends Controller
{
    public function __construct(private ViteAssetResolver $vite)
    {
    }

    public function index(?int $id = null): Response
    {
        return $this->renderShell('blocks', 'data-block-id', $id);
    }

    public function elements(?int $id = null): Response
    {
        return $this->renderShell('elements', 'data-draft-id', $id);
    }

    /**
     * Legacy alias for the old /designer route — now serves the unified shell
     * pre-selected on the Elements tab.
     */
    public function designer(?int $id = null): Response
    {
        return $this->elements($id);
    }

    private function renderShell(string $tab, string $idAttr, ?int $id): Response
    {
        // Belt-and-braces: the nav entry sits under "mtc. only" which the
        // existing Menu::getAdminMenu() filter hides from non-MTC admins, but
        // the URL itself stayed reachable until now. Mirror that gate at the
        // controller so a shared link can't bypass it.
        if (defined('MTCADMIN_USERID') && (int) ($_SESSION['adminId'] ?? 0) !== (int) MTCADMIN_USERID) {
            abort(403, 'Block Manager is restricted to MTC admins.');
        }

        $idValue = $id !== null ? (string) $id : '';
        $scriptHref = $this->vite->asset('sites/default/js/block_manager_admin.js');

        // Site app.css carries every element variant's styles — needed so the
        // composer's inline preview matches the live frontend. Loaded BEFORE
        // the admin stylesheet so admin rules can override any chrome-element
        // collisions (e.g. `#header { background: transparent }` in app.css vs
        // the legacy mtc-blue header) — see the chrome-guard block in
        // block_manager_admin.less.
        $siteCssHrefs = $this->vite->css('sites/default/js/app.js');
        $adminCssHrefs = $this->vite->css('sites/default/js/block_manager_admin.js');

        $cssTags = '';
        foreach (array_merge($siteCssHrefs, $adminCssHrefs) as $href) {
            $cssTags .= '<link rel="stylesheet" href="' . htmlspecialchars($href, ENT_QUOTES) . '">';
        }

        $tabAttr = htmlspecialchars($tab, ENT_QUOTES);
        $idAttrName = htmlspecialchars($idAttr, ENT_QUOTES);
        $idAttrValue = htmlspecialchars($idValue, ENT_QUOTES);
        $title = $tab === 'elements' ? 'Block Manager — Elements' : 'Block Manager — Blocks';

        // The SPA mount div + its asset tags — emitted into either the legacy
        // admin chrome (real request) or the standalone fallback (test/CLI).
        $body = $cssTags
            . '<div id="block-manager-app" '
            . 'data-initial-tab="' . $tabAttr . '" '
            . $idAttrName . '="' . $idAttrValue . '"></div>'
            . '<script type="module" src="' . htmlspecialchars($scriptHref, ENT_QUOTES) . '"></script>';

        $wrapped = $this->wrapInAdminChrome($title, $body);
        if ($wrapped !== null) {
            return response($wrapped, 200);
        }

        // Fallback standalone shell — used by per-test containers (where the
        // legacy `$twig` global isn't carried) and by any CLI render.
        return response($this->renderStandalone($title, $cssTags, $body), 200);
    }

    /**
     * Wrap the SPA body in the legacy admin chrome (top bar + mtc. only nav)
     * by output-buffering adminheader.html.php + body + adminfooter.html.php.
     * Returns null when the chrome can't be rendered (e.g. a per-test container
     * with no `twig` binding), so the caller can fall back to the bare shell.
     */
    private function wrapInAdminChrome(string $title, string $bodyHtml): ?string
    {
        // The chrome's main.nav.php uses `$twig` to render the admin nav. If
        // the per-request container doesn't have it bound (tests / CLI), don't
        // try — fall through to the bare standalone shell.
        if (!app()->bound('twig') || !defined('SITE_PATH')) {
            return null;
        }

        // adminheader.html.php (and the partials it includes) build paths as
        // `$path . 'core/...'`. Legacy admin pages set `$path = '../../../'`
        // and rely on the CWD being their own folder. Laravel's CWD is the
        // project root, so we use an absolute prefix instead — PHP's
        // `require` accepts absolute paths happily and the internal includes
        // resolve correctly.
        $path = SITE_PATH . '/';

        // The chrome reads these globals from its caller's scope.
        global $adminuser, $new_message_count, $twig, $page_meta;
        if (!isset($twig)) {
            $twig = app('twig');
        }
        if (!isset($page_meta) || !is_array($page_meta)) {
            $page_meta = ['title' => $title];
        } else {
            $page_meta['title'] = $title;
        }

        try {
            ob_start();
            require SITE_PATH . '/core/admin/templates/adminheader.html.php';
            echo $bodyHtml;
            require SITE_PATH . '/core/admin/templates/adminfooter.html.php';
            return ob_get_clean();
        } catch (\Throwable $_e) {
            // Any rendering hiccup → bare shell rather than a 500.
            if (ob_get_level() > 0) {
                ob_end_clean();
            }
            return null;
        }
    }

    private function renderStandalone(string $title, string $cssTags, string $bodyHtml): string
    {
        return <<<HTML
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{$title}</title>
    {$cssTags}
</head>
<body>
{$bodyHtml}
</body>
</html>
HTML;
    }
}
