<?php

declare(strict_types=1);

/**
 * MyronPrimitiveRenderer — single canonical render path for primitives.
 *
 * Replaces the historical dual-path situation (component viewer's bespoke specimens
 * + DefinitionTemplateRenderer's per-section methods) with one entry point:
 *
 *   `renderById(string $primitiveId, array $props, array $archetypeContext): string`
 *
 * Where:
 *   - `$primitiveId` is the registry id (e.g. 'hero-with-cta', 'cms-content-body').
 *   - `$props` carries section-level inputs: variant, title, body, notes, content payload.
 *   - `$archetypeContext` carries: bundleId, templateId, basePath. Funnel/news/ecom
 *     specialization is resolved at render time based on these — no separate component
 *     classification.
 *
 * In R4a this class is a façade over `MyronDefinitionTemplateRenderer::renderSingleSection()`.
 * Per the APO-061 contract, R4b migrates the per-section methods themselves into this class
 * and reduces the renderer to a thin loop. R4c then deletes the regex-extraction kludge in
 * `MyronComponentPreviewRenderer` and routes the viewer through this same class.
 */
final class MyronPrimitiveRenderer
{
    public function __construct(
        private readonly MyronDefinitionTemplateRenderer $templateRenderer,
    ) {
    }

    /**
     * @param array<string, mixed> $props
     * @param array<string, mixed> $archetypeContext
     */
    public function renderById(string $primitiveId, array $props = [], array $archetypeContext = []): string
    {
        $id = trim($primitiveId);
        if ($id === '') {
            return '';
        }

        return $this->templateRenderer->renderSingleSection($id, $props, $archetypeContext);
    }

    /**
     * Returns true when this renderer has a non-fallthrough branch for the primitive id.
     * After APO-058's coverage sweep this is true for every id referenced by any
     * packaged template; consumers can use it to decide whether to fall back to
     * a viewer-side specimen for atom-shaped primitives that aren't section-shaped.
     */
    public function canRender(string $primitiveId): bool
    {
        $id = trim($primitiveId);
        if ($id === '') {
            return false;
        }

        // Render once with neutral inputs; the generic-fallthrough signature
        // is the presence of `<span class="myron-definition-section__component">$id</span>`
        // outside a funnel-rendered branch.
        $html = $this->renderById($id, [], []);
        $marker = '<span class="myron-definition-section__component">' . htmlspecialchars($id, ENT_QUOTES, 'UTF-8') . '</span>';
        if (!str_contains($html, $marker)) {
            return true;
        }

        // Funnel branch legitimately emits the same span — accept it.
        return str_contains($html, 'data-funnel-hook=');
    }
}
