<?php

namespace Daylight\Core\Modules\PageBuilder\Concerns;

use Daylight\Core\Modules\PageBuilder\Registries\ComponentRegistry;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

trait InteractsWithPageBuilder
{
    public function getPageBuilderRules(string $fieldName = 'components', bool $required = true): array
    {
        $rules = [
            $fieldName => $required ? ['required', 'array'] : ['sometimes', 'nullable'],
            $fieldName . '.*.id' => ['sometimes', 'nullable'],
            $fieldName . '.*.type' => ['required', 'string', 'max:255'],
            $fieldName . '.*.data' => ['required', 'array'],
        ];

        $registry = app(ComponentRegistry::class);
        $components = $this->get($fieldName, []);

        if (is_string($components)) {
            $components = json_decode($components, true) ?: [];
        }

        if (! is_array($components)) {
            $components = [];
        }

        foreach ($components as $index => $component) {
            if (! isset($component['type']) || ! is_array($component)) {
                continue;
            }

            $definition = $registry->getDefinition($component['type']);
            if (! $definition) {
                continue;
            }

            if (isset($component['data']) && is_array($component['data'])) {
                $validFieldNames = $definition->fields()->pluck('name')->toArray();
                $component['data'] = Arr::only($component['data'], $validFieldNames);
            }

            foreach ($definition->fields() as $field) {
                $fieldPath = "{$fieldName}.{$index}.data.{$field->name}";
                $fieldRules = [];

                $isRequired = isset($field->options['required']) && $field->options['required'];

                switch ($field->type) {
                    case 'text':
                    case 'wysiwyg':
                        $fieldRules[] = $isRequired ? 'required' : 'nullable';
                        $fieldRules[] = 'string';
                        if (isset($field->options['max'])) {
                            $fieldRules[] = 'max:' . $field->options['max'];
                        }
                        break;

                    case 'select':
                        $fieldRules[] = $isRequired ? 'required' : 'nullable';
                        $fieldRules[] = 'string';
                        break;

                    case 'button':
                        $rules["{$fieldPath}.url"] = ['nullable', 'string', 'max:255'];
                        $rules["{$fieldPath}.label"] = ['nullable', 'string', 'max:255'];
                        break;

                    case 'media':
                        $fieldRules[] = $isRequired ? 'required' : 'nullable';
                        $fieldRules[] = 'array';

                        if ($isRequired) {
                            $fieldRules[] = 'min:1';
                        }

                        if (isset($field->amountOfFiles) && $field->amountOfFiles === 1) {
                            $fieldRules[] = 'max:1';
                        }

                        $rules["{$fieldPath}.*.id"] = ['sometimes', 'required', 'exists:media,id'];
                        break;

                    case 'repeater':
                        $fieldRules[] = $isRequired ? 'required' : 'nullable';
                        $fieldRules[] = 'array';

                        foreach ($field->items as $subField) {
                            $subFieldPath = "{$fieldPath}.*.{$subField->name}";
                            $subFieldRules = [];

                            $subIsRequired = isset($subField->options['required']) && $subField->options['required'];

                            if ($subField->type === 'media') {
                                $subFieldRules[] = $subIsRequired ? 'required' : 'nullable';
                                $subFieldRules[] = 'array';
                                if ($subIsRequired) {
                                    $subFieldRules[] = 'min:1';
                                }
                                $rules["{$subFieldPath}.*.id"] = ['sometimes', 'required', 'exists:media,id'];
                            } else {
                                $subFieldRules[] = $subIsRequired ? 'required' : 'nullable';
                                $subFieldRules[] = 'string';
                            }

                            $rules[$subFieldPath] = $subFieldRules;
                        }
                        break;

                    case 'related':
                        $fieldRules[] = $isRequired ? 'required' : 'nullable';
                        $fieldRules[] = 'array';

                        $rules["{$fieldPath}.*.id"] = ['required', 'integer'];
                        break;
                }

                if (! empty($fieldRules)) {
                    $rules[$fieldPath] = $fieldRules;
                }
            }
        }

        return $rules;
    }

    public function prepareForValidation(): void
    {
        $registry = app(ComponentRegistry::class);

        // JSON hidden inputs are the single source of truth - they contain ALL data including media/related
        // (kept in sync by Livewire's morph algorithm)
        $jsonComponents = $this->input('components_json', []);
        $components = [];

        if (! empty($jsonComponents) && is_array($jsonComponents)) {
            foreach ($jsonComponents as $jsonComponent) {
                if (is_string($jsonComponent)) {
                    $decoded = json_decode($jsonComponent, true);
                    if (is_array($decoded)) {
                        $components[] = $decoded;
                    }
                } elseif (is_array($jsonComponent)) {
                    $components[] = $jsonComponent;
                }
            }
        }

        // Fallback to form components if JSON textareas are empty
        if (empty($components)) {
            $formComponents = $this->input('components', []);
            $components = is_array($formComponents) ? $formComponents : [];
        }

        if (is_string($components)) {
            $components = json_decode($components, true) ?: [];
        }

        // Normalize components: ensure correct types for media/related/repeater fields
        if (is_array($components)) {
            $components = collect($components)->map(function ($component) use ($registry) {
                if (! is_array($component) || ! isset($component['type'])) {
                    return null;
                }

                $definition = $registry->getDefinition($component['type']);

                if ($definition && isset($component['data']) && is_array($component['data'])) {
                    foreach ($definition->fields() as $field) {
                        $fieldName = $field->name;

                        // Normalize media fields: ensure arrays, decode JSON strings
                        if ($field->type === 'media') {
                            $component['data'][$fieldName] = $this->normalizeArrayField(
                                $component['data'][$fieldName] ?? null
                            );
                        }

                        // Normalize related fields
                        if ($field->type === 'related') {
                            $component['data'][$fieldName] = $this->normalizeArrayField(
                                $component['data'][$fieldName] ?? null
                            );
                        }

                        // Normalize repeater fields and their nested media
                        if ($field->type === 'repeater' && isset($component['data'][$fieldName]) && is_array($component['data'][$fieldName])) {
                            foreach ($component['data'][$fieldName] as $rowIndex => $row) {
                                if (! is_array($row)) {
                                    continue;
                                }

                                foreach ($field->items as $subField) {
                                    if ($subField->type === 'media') {
                                        $component['data'][$fieldName][$rowIndex][$subField->name] = $this->normalizeArrayField(
                                            $row[$subField->name] ?? null
                                        );
                                    }
                                }
                            }
                        }
                    }
                }

                return $component;
            })->filter()->values()->toArray();
        }

        $this->merge([
            'components' => $components,
        ]);
    }

    public function getComponentsFromRequest(string $fieldName = 'components'): Collection
    {
        $registry = app(ComponentRegistry::class);

        return collect($this->get($fieldName))
            ->map(function (array $component, $index) use ($registry) {
                $definition = $registry->getDefinition($component['type'] ?? '');

                if ($definition && isset($component['data']) && is_array($component['data'])) {
                    $validFieldNames = $definition->fields()->pluck('name')->toArray();
                    $component['data'] = Arr::only($component['data'], $validFieldNames);
                }

                return [
                    ...Arr::only($component, ['id', 'type', 'data']),
                    'order' => $index,
                ];
            })->values();
    }

    private function normalizeArrayField(mixed $value): array
    {
        if ($value === null || $value === '' || $value === '[]') {
            return [];
        }

        if (is_string($value)) {
            $decoded = json_decode($value, true);

            return is_array($decoded) ? $decoded : [];
        }

        if (is_array($value)) {
            return $value;
        }

        return [];
    }
}
