<?php

namespace App\Http\Controllers;

use App\Domain\Shared\Models\Academy;
use App\Domain\Website\Models\WebsitePage;
use App\Domain\Website\Services\BlockRenderer;
use App\Domain\Website\Services\WebsitePageService;
use App\Domain\Website\Services\WebsiteSettingService;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Renders builder pages (website_pages + website_blocks).
 *
 * Runs after every other web route so a page slug can never shadow a real
 * application route; WebsitePageService additionally refuses reserved slugs.
 */
class WebsitePageController extends Controller
{
    public function __construct(
        private readonly WebsiteSettingService $settings,
    ) {}

    /**
     * Fallback entry point. Route::fallback passes no route parameters, so the
     * slug is taken from the request path rather than a bound argument.
     */
    public function fallback(Request $request)
    {
        // `/en/about-us` and `/about-us` address the same page in different
        // languages. SetLocale has already read the prefix; here it is removed
        // so the slug lookup sees the page, not the language.
        $slug = website_strip_locale($request->path());

        // A bare locale (`/en`) is that language's homepage, not a missing page.
        if ($slug === '' || $slug === '/') {
            if (website_locale_from_path($request->path()) !== null) {
                // Same handoff as "/": an unmigrated tenant keeps its legacy site
                // under a locale prefix rather than 404-ing on it.
                return $this->homeOrLegacy($request, app(PublicWebsiteController::class));
            }

            throw new NotFoundHttpException;
        }

        // A reserved prefix belongs to the application, not to the page builder.
        // Without this the fallback answers 200 for every unrouted path under it —
        // so a removed or mistyped endpoint returns a website page instead of 404,
        // which hides deploy mistakes and defeats client-side route validation.
        $first = strtok($slug, '/');

        if (in_array($first, WebsitePageService::RESERVED_SLUGS, true)) {
            throw new NotFoundHttpException;
        }

        return $this->show($request, $slug);
    }

    public function show(Request $request, string $slug)
    {
        $academy = $this->academy();

        $page = WebsitePage::query()
            ->where('slug', $slug)
            ->when(! $this->canPreview($request), fn ($q) => $q->where('is_published', true))
            ->first();

        if (! $page) {
            throw new NotFoundHttpException;
        }

        return $this->renderPage($academy, $page);
    }

    /** Homepage, when the academy has promoted a builder page to be it. */
    public function home(Request $request)
    {
        $academy = $this->academy();

        $page = WebsitePage::query()
            ->where('is_homepage', true)
            ->when(! $this->canPreview($request), fn ($q) => $q->where('is_published', true))
            ->first();

        if (! $page) {
            throw new NotFoundHttpException;
        }

        return $this->renderPage($academy, $page);
    }

    /**
     * Entry point for "/". Serves a builder homepage when one exists, otherwise
     * hands off to the legacy section renderer so already-live tenants that have
     * not migrated keep their site exactly as it is.
     */
    public function homeOrLegacy(Request $request, PublicWebsiteController $legacy)
    {
        $hasBuilderHome = WebsitePage::query()
            ->where('is_homepage', true)
            ->where('is_published', true)
            ->exists();

        return $hasBuilderHome ? $this->home($request) : $legacy->home();
    }

    /** Draft preview for staff who can manage the site. */
    public function preview(Request $request, WebsitePage $page)
    {
        abort_unless($request->user()?->can('settings.manage'), 403);

        return $this->renderPage($this->academy(), $page, preview: true);
    }

    private function renderPage(Academy $academy, WebsitePage $page, bool $preview = false)
    {
        app()->instance('current_academy', $academy);

        $page->load(['blocks.childrenRecursive']);

        $settings = $this->settings->getOrCreate($academy);

        /*
         * Blocks are rendered to HTML here, before the layout runs — not from
         * inside the layout's @section.
         *
         * BlockRenderer catches a failing block so the rest of the site stays
         * up, but Laravel's View::render() calls flushState() whenever a view
         * throws, and that clears the section stack of the page *around* it.
         * Rendered inside @section('content'), one bad block therefore killed
         * the whole page at @endsection with an unrelated error, defeating the
         * very isolation the catch exists to provide. Out here there is no open
         * section to corrupt.
         */
        $content = app(BlockRenderer::class)->renderPage($page, [
            'academy' => $academy,
            'settings' => $settings,
            'page' => $page,
        ]);

        return view('website.page', [
            'academy' => $academy,
            'settings' => $settings,
            'page' => $page,
            'preview' => $preview,
            'content' => $content,
        ]);
    }

    private function academy(): Academy
    {
        $academy = app()->bound('current_academy')
            ? app('current_academy')
            : Academy::first();

        abort_unless($academy, 404);

        return $academy;
    }

    private function canPreview(Request $request): bool
    {
        return (bool) $request->user()?->can('settings.manage');
    }
}
