<?php

namespace Formatrica\Fields;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Registry of field-type renderers contributed by an edition beyond the Free
 * core set (docs/wporg-free-runtime-separation.md §6.3).
 *
 * Form_Renderer renders Free field types inline and consults this registry only
 * for types it does not handle itself. The Pro package registers its field
 * renderers into the shared default instance during boot; a Free package
 * registers none, so an unknown type resolves to null and Form_Renderer emits
 * '' (fail closed — never a text-input fallback).
 */
final class Field_Renderer_Registry
{
    /** @var array<string, Field_Renderer_Interface> */
    private array $renderers = [];

    private static ?Field_Renderer_Registry $default = null;

    /**
     * Field types Form_Renderer implements natively in every edition. Kept
     * here so the renderer and the validator share ONE availability decision:
     * a field this edition cannot render must not be validated either —
     * otherwise a required Pro field on a downgraded install would be
     * invisible on the public form yet block every submission.
     */
    private const NATIVE_TYPES = [
        'text', 'email', 'tel', 'password', 'url', 'number', 'range', 'color',
        'datetime', 'textarea', 'checkbox', 'select', 'radio', 'file', 'date',
        'hidden', 'country', 'state', 'custom_text', 'button',
    ];

    public function register(Field_Renderer_Interface $renderer): void
    {
        $this->renderers[$renderer->type()] = $renderer;
    }

    public function get(string $type): ?Field_Renderer_Interface
    {
        return $this->renderers[$type] ?? null;
    }

    /**
     * Can this edition render (and therefore expect input for) $type?
     * Native Free types always; anything else only when a renderer registered.
     */
    public function is_available(string $type): bool
    {
        return in_array($type, self::NATIVE_TYPES, true) || isset($this->renderers[$type]);
    }

    /**
     * Shared instance used by Form_Renderer and populated by the Pro package.
     */
    public static function default(): self
    {
        if (self::$default === null) {
            self::$default = new self();
        }

        return self::$default;
    }

    /** Test seam. */
    public static function set_default(?self $registry): void
    {
        self::$default = $registry;
    }
}
