<?php
declare(strict_types=1);
namespace PpPhp\Core;

/**
 * ValidationPlugin
 *
 * A single, self-contained validation rule (Required, Email, Phone, …).
 * Drop a folder in plugins/Validation/{Name}/Plugin.php and it appears in the
 * designer's per-field "Validation" section automatically — same discovery
 * model as the View / Edit / Filter field-control plugins.
 *
 * Each rule declares BOTH sides of the check so client + server stay in sync:
 *   - serverCheck()  runs in the generated app (authoritative, on save)
 *   - clientRule()   projects the rule onto a small generic vocabulary the
 *                    in-page validator understands (pattern / minLength / words / …)
 *
 * Empty values are the "required" rule's business: every OTHER rule must treat
 * an empty value as valid (skip), so optional fields don't fire format errors
 * when left blank. The Validator runtime enforces this centrally.
 */
interface ValidationPlugin
{
    /** Stable id, e.g. 'email'. Referenced by a field's validations[].type. */
    public static function id(): string;

    /** Human label for the picker, e.g. 'Email address'. */
    public static function label(): string;

    /** Grouping for the picker menu, e.g. 'Format', 'Numeric', 'Advanced'. */
    public static function group(): string;

    /**
     * Parameter schema (same shape the field-control settings UI already speaks:
     * [ ['key'=>..,'type'=>'text|number|bool|select','label'=>..,'default'=>..], … ]).
     * Empty array = no parameters.
     */
    public static function paramsSchema(): array;

    /**
     * Field input-types this rule is offered for in the picker (hint only, the
     * user can always add any rule). Empty array = applies to everything.
     * e.g. ['number','currency'] or ['email','text'].
     */
    public static function appliesTo(): array;

    /** Default error message. Supports {label} and {paramKey} placeholders. */
    public static function defaultMessage(): string;

    /**
     * Server-side check. Return true when VALID. The value is guaranteed
     * non-empty UNLESS the rule opts in with an optional static
     * `validatesEmpty(): bool` returning true (required, mustAccept).
     *
     * A rule needing DB/table context (unique) may declare an extra optional
     * 4th param `array $context = []` ({ table, field, id }); the runtime always
     * passes it and rules that don't declare it simply ignore it.
     *
     * @param array<string,mixed> $params  resolved parameters (schema defaults applied)
     * @param array<string,mixed> $record  the full row being saved (for cross-field rules)
     */
    public static function serverCheck(mixed $value, array $params, array $record = []): bool;

    /**
     * Client-side projection: a small assoc array the in-page validator reads.
     * Recognised keys: required, pattern, flags, words, minLength, maxLength,
     * min, max, integer, number, match, checked. Return [] for server-only rules.
     *
     * @param array<string,mixed> $params
     * @return array<string,mixed>
     */
    public static function clientRule(array $params): array;
}
