<?php

namespace App\Domain\Website\Blocks;

/**
 * Declarative description of one editable field on a block type.
 *
 * Field definitions are the contract between a block type and the builder UI:
 * the UI renders inputs from these, and validation rules are derived from these,
 * so a new block type needs no bespoke form and no migration.
 */
class BlockField
{
    public bool $translatable = false;

    public bool $required = false;

    public mixed $default = null;

    public ?string $help = null;

    public array $options = [];

    /** @var BlockField[] Sub-fields, for Repeater fields only. */
    public array $fields = [];

    public ?string $showIf = null;

    public mixed $showIfValue = null;

    public ?int $max = null;

    public ?int $min = null;

    public ?string $placeholder = null;

    final public function __construct(
        public readonly string $key,
        public readonly string $label,
        public readonly FieldType $type,
    ) {
        $this->translatable = $type->isTranslatable();
    }

    public static function make(string $key, string $label, FieldType $type): static
    {
        return new static($key, $label, $type);
    }

    public static function text(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Text);
    }

    public static function textarea(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Textarea);
    }

    public static function richText(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::RichText);
    }

    public static function image(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Image);
    }

    public static function gallery(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Gallery);
    }

    public static function video(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Video);
    }

    public static function icon(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Icon);
    }

    public static function link(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Link);
    }

    public static function color(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Color);
    }

    public static function toggle(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Toggle)->default(false);
    }

    public static function number(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Number);
    }

    public static function date(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Date);
    }

    public static function code(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Code);
    }

    public static function map(string $key, string $label): static
    {
        return static::make($key, $label, FieldType::Map);
    }

    public static function alignment(string $key = 'align', string $label = 'المحاذاة'): static
    {
        return static::make($key, $label, FieldType::Alignment)
            ->options(['start' => 'البداية', 'center' => 'الوسط', 'end' => 'النهاية'])
            ->default('center');
    }

    public static function select(string $key, string $label, array $options): static
    {
        return static::make($key, $label, FieldType::Select)->options($options);
    }

    public static function radio(string $key, string $label, array $options): static
    {
        return static::make($key, $label, FieldType::Radio)->options($options);
    }

    /** @param  BlockField[]  $fields */
    public static function repeater(string $key, string $label, array $fields): static
    {
        $f = static::make($key, $label, FieldType::Repeater);
        $f->fields = $fields;
        $f->default = [];

        return $f;
    }

    /** Binds the block to live ERP data (branches, programs, trainers, ...). */
    public static function dataSource(string $key, string $label, array $sources): static
    {
        return static::make($key, $label, FieldType::DataSource)->options($sources);
    }

    public function required(bool $v = true): static
    {
        $this->required = $v;

        return $this;
    }

    public function default(mixed $v): static
    {
        $this->default = $v;

        return $this;
    }

    public function help(string $v): static
    {
        $this->help = $v;

        return $this;
    }

    public function options(array $v): static
    {
        $this->options = $v;

        return $this;
    }

    public function placeholder(string $v): static
    {
        $this->placeholder = $v;

        return $this;
    }

    public function translatable(bool $v = true): static
    {
        $this->translatable = $v;

        return $this;
    }

    public function max(int $v): static
    {
        $this->max = $v;

        return $this;
    }

    public function min(int $v): static
    {
        $this->min = $v;

        return $this;
    }

    /** Only show this field in the builder when another field has a given value. */
    public function showIf(string $otherKey, mixed $value = true): static
    {
        $this->showIf = $otherKey;
        $this->showIfValue = $value;

        return $this;
    }

    /** Laravel validation rules for this field, keyed by the data path. */
    public function validationRules(string $prefix = 'data'): array
    {
        $path = "{$prefix}.{$this->key}";
        $rules = [];

        if ($this->type === FieldType::Repeater) {
            $rules[$path] = 'array'.($this->max ? "|max:{$this->max}" : '');
            foreach ($this->fields as $sub) {
                $rules += $sub->validationRules("{$path}.*");
            }

            return $rules;
        }

        $base = $this->required ? 'required' : 'nullable';

        if ($this->translatable) {
            // Arabic is the primary locale, so it carries the `required`.
            $rules["{$path}.ar"] = $base.'|string';
            $rules["{$path}.en"] = 'nullable|string';

            return $rules;
        }

        $rules[$path] = match ($this->type) {
            FieldType::Number => $base.'|numeric',
            // Link targets are rendered into href attributes, so the scheme is
            // constrained here as well as defensively at render time.
            FieldType::Link => [$base, 'string', 'max:500', 'regex:/^\s*(https?:\/\/|mailto:|tel:|\/(?!\/)|#|\?)/i'],
            FieldType::Toggle => 'boolean',
            FieldType::Color => $base.'|string|max:30',
            FieldType::Gallery => $base.'|array',
            FieldType::Select, FieldType::Radio, FieldType::Alignment => $this->options
                ? $base.'|in:'.implode(',', array_keys($this->options))
                : $base.'|string',
            default => $base.'|string',
        };

        return $rules;
    }
}
