<?php

namespace App\Domain\Website\Services;

use App\Domain\Website\Models\WebsiteBlock;
use App\Domain\Website\Models\WebsitePage;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Facades\Log;
use Throwable;

/**
 * Turns a stored block tree into HTML.
 *
 * A block that fails to render is skipped with a logged warning rather than
 * taking the whole public page down — a tenant's site staying up matters more
 * than any single section.
 */
class BlockRenderer
{
    public function __construct(
        private readonly BlockDataResolver $data,
    ) {}

    /** Renders every top-level block on a page. */
    public function renderPage(WebsitePage $page, array $context = []): HtmlString
    {
        $blocks = $page->relationLoaded('blocks')
            ? $page->blocks
            : $page->blocks()->with('childrenRecursive')->get();

        return $this->renderMany($blocks, $context);
    }

    public function renderMany(Collection $blocks, array $context = []): HtmlString
    {
        $html = '';

        foreach ($blocks as $block) {
            $html .= $this->render($block, $context)->toHtml();
        }

        return new HtmlString($html);
    }

    public function render(WebsiteBlock $block, array $context = []): HtmlString
    {
        if (! $block->isRenderable()) {
            return new HtmlString('');
        }

        $definition = $block->definition();

        try {
            $inner = view($definition->viewFor($block->variant), array_merge($context, [
                'block' => $block,
                'definition' => $definition,
                'variant' => $block->variant,
                'renderer' => $this,
                'items' => $this->data->for($block, $context),
                // Pristine page-level context, for container blocks to hand to
                // their children without leaking their own local variables.
                'ctx' => $context,
            ]))->render();
        } catch (Throwable $e) {
            Log::warning('Website block failed to render', [
                'block_id' => $block->id,
                'type' => $block->type,
                'variant' => $block->variant,
                'message' => $e->getMessage(),
            ]);

            return new HtmlString(
                app()->hasDebugModeEnabled()
                    ? '<!-- block '.e($block->type).' failed: '.e($e->getMessage()).' -->'
                    : ''
            );
        }

        // The shell owns background, padding, animation and visibility so that
        // every block type gets them without repeating the markup.
        return new HtmlString(view('website.blocks._shell', [
            'block' => $block,
            'definition' => $definition,
            'content' => new HtmlString($inner),
        ])->render());
    }

    /** Renders the children of a container block, optionally limited to one slot. */
    public function renderChildren(WebsiteBlock $block, string $slot = 'default', array $context = []): HtmlString
    {
        $children = $block->relationLoaded('children')
            ? $block->children->where('slot', $slot)
            : $block->children()->where('slot', $slot)->get();

        return $this->renderMany(collect($children), $context);
    }
}
