<?php
declare(strict_types=1);

namespace PpPhp\Core\Designer\Property;

final class DesignerPropertySchema
{
    public function __construct(
        private readonly string $componentType,
        private readonly array $sections = [],
        private readonly array $metadata = []
    ) {}

    public static function fromComponentDefinition(array $definition, DesignerPropertyEditorRegistry $editors): self
    {
        $sections = [
            'general' => DesignerPropertySection::create('general', 'General', [], 10),
            'layout' => DesignerPropertySection::create('layout', 'Layout', [], 20),
            'style' => DesignerPropertySection::create('style', 'Style', [], 30),
            'data' => DesignerPropertySection::create('data', 'Data', [], 40),
            'events' => DesignerPropertySection::create('events', 'Events', [], 50),
            'visibility' => DesignerPropertySection::create('visibility', 'Visibility', [], 60),
            'resources' => DesignerPropertySection::create('resources', 'Resources', [], 70),
            'advanced' => DesignerPropertySection::create('advanced', 'Advanced', [], 80),
        ];

        $sections['general'] = $sections['general']
            ->withField(self::field('id', 'ID', 'readonly', null, $editors, true))
            ->withField(self::field('label', 'Label', 'text', $definition['title'] ?? '', $editors));

        foreach ((array) ($definition['properties'] ?? []) as $property) {
            $property = (array) $property;
            $section = (string) ($property['section'] ?? self::inferSection((string) ($property['type'] ?? 'text')));
            if (!isset($sections[$section])) {
                $sections[$section] = DesignerPropertySection::create($section, ucfirst($section), [], 90);
            }
            $sections[$section] = $sections[$section]->withField(self::field(
                (string) ($property['name'] ?? 'property'),
                (string) ($property['label'] ?? $property['name'] ?? 'Property'),
                (string) ($property['type'] ?? 'text'),
                $property['default'] ?? null,
                $editors,
                (bool) ($property['required'] ?? false),
                (array) ($property['options'] ?? [])
            ));
        }

        $sections['style'] = $sections['style']
            ->withField(self::field('class', 'CSS Class', 'text', '', $editors))
            ->withField(self::field('margin', 'Margin', 'spacing', '', $editors))
            ->withField(self::field('padding', 'Padding', 'spacing', '', $editors))
            ->withField(self::field('color', 'Text Color', 'color', '', $editors))
            ->withField(self::field('backgroundColor', 'Background', 'color', '', $editors));

        $sections['visibility'] = $sections['visibility']
            ->withField(self::field('hidden', 'Hidden', 'boolean', false, $editors))
            ->withField(self::field('locked', 'Locked', 'boolean', false, $editors))
            ->withField(self::field('condition', 'Condition', 'code', '', $editors));

        $sections['resources'] = $sections['resources']
            ->withField(self::field('icon', 'Icon', 'icon', '', $editors));

        $sections['advanced'] = $sections['advanced']
            ->withField(self::field('meta', 'Metadata', 'json', [], $editors));

        $sections = array_values($sections);
        usort($sections, static fn (DesignerPropertySection $a, DesignerPropertySection $b): int => $a->order() <=> $b->order());

        return new self((string) ($definition['type'] ?? 'component'), array_map(static fn (DesignerPropertySection $section): array => $section->toArray(), $sections), [
            'definitionVersion' => $definition['version'] ?? '1.0.0',
        ]);
    }

    public function toArray(): array
    {
        return [
            'componentType' => $this->componentType,
            'sections' => $this->sections,
            'metadata' => $this->metadata,
        ];
    }

    private static function field(string $name, string $label, string $type, mixed $default, DesignerPropertyEditorRegistry $editors, bool $required = false, array $options = []): array
    {
        $editor = $editors->get($type) ?? $editors->get('text');
        return [
            'name' => $name,
            'label' => $label,
            'type' => $type,
            'editor' => $editor ? $editor->toArray() : null,
            'default' => $default,
            'options' => $options,
            'required' => $required,
        ];
    }

    private static function inferSection(string $type): string
    {
        return match ($type) {
            'datasource' => 'data',
            'workflow', 'event', 'code' => 'events',
            'icon', 'image' => 'resources',
            'color', 'spacing', 'border', 'shadow' => 'style',
            default => 'general',
        };
    }
}
