<?php

namespace Daylight\Core\Modules\PageBuilder\Services;

use Daylight\Core\Modules\PageBuilder\Contracts\HasComponents;
use Daylight\Core\Modules\PageBuilder\Entities\Component as ComponentDefinition;
use Daylight\Core\Modules\PageBuilder\Models\Component;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class ComponentHandler
{
    private Collection $relatedFields;

    private Collection $mediaFields;

    private Collection $repeaterFields;

    public function __construct(
        private array $data,
        private ComponentDefinition $definition,
        private HasComponents $owner,
        private int $order
    ) {
        // Cache field type collections once
        $fields = $definition->fields();
        $this->relatedFields = $fields->where('type', 'related');
        $this->mediaFields = $fields->where('type', 'media');
        $this->repeaterFields = $fields->where('type', 'repeater');
    }

    public function save(): int
    {
        // 1. Prepare clean data (strip media/related from JSON)
        $cleanData = $this->prepareCleanData();

        // 2. Save component model
        $component = Component::updateOrCreate(
            ['id' => $this->data['id'] ?? null],
            [
                'buildable_id' => $this->owner->id,
                'buildable_type' => get_class($this->owner),
                'type' => $this->data['type'],
                'data' => $cleanData,
                'order' => $this->order,
            ]
        );

        // 3. Sync relationships (more efficient batching)
        $this->syncRelations($component);
        $this->syncMedia($component);

        return $component->id;
    }

    private function prepareCleanData(): array
    {
        $data = $this->data['data'];

        // Get all valid field names from the component definition
        $allFields = $this->definition->fields();
        $validFieldNames = $allFields->pluck('name')->toArray();

        // Filter data to only include fields that belong to this component type
        $data = Arr::only($data, $validFieldNames);

        // Remove media and related fields (stored in pivot tables)
        foreach ($this->mediaFields as $field) {
            unset($data[$field->name]);
        }

        foreach ($this->relatedFields as $field) {
            unset($data[$field->name]);
        }

        // Clean repeater rows (remove nested media and invalid fields)
        foreach ($this->repeaterFields as $field) {
            if (! isset($data[$field->name])) {
                continue;
            }

            // Get valid nested field names for this repeater
            $validNestedFieldNames = collect($field->items)->pluck('name')->toArray();
            $mediaFieldNames = collect($field->items)
                ->where('type', 'media')
                ->pluck('name');

            $data[$field->name] = collect($data[$field->name])
                ->map(function ($row) use ($validNestedFieldNames, $mediaFieldNames) {
                    // Filter to only include valid nested fields
                    $filteredRow = Arr::only($row, $validNestedFieldNames);

                    // Remove nested media fields
                    return Arr::except($filteredRow, $mediaFieldNames->toArray());
                })
                ->toArray();
        }

        return $data;
    }

    private function syncRelations(Component $component): void
    {
        if ($this->relatedFields->isEmpty()) {
            return;
        }

        $relations = $this->relatedFields
            ->filter(fn ($f) => isset($this->data['data'][$f->name]))
            ->flatMap(function ($field) {
                $items = $this->data['data'][$field->name];

                // Ensure it's an array
                if (! is_array($items)) {
                    return collect();
                }

                return collect($items)
                    ->map(function ($item, $index) use ($field) {
                        // Handle both array format {id: 1, name: 'Product'} and simple ID format
                        $id = is_array($item) ? ($item['id'] ?? null) : $item;

                        if (! $id) {
                            return null;
                        }

                        return [
                            'relatable_id' => $id,
                            'relatable_type' => $field->relatedType,
                            'order' => $index,
                        ];
                    })
                    ->filter();
            });

        $component->relatables()->delete();
        if ($relations->isNotEmpty()) {
            $component->relatables()->createMany($relations->toArray());
        }
    }

    private function syncMedia(Component $component): void
    {
        $mediaAttachments = collect();

        // Direct media fields
        foreach ($this->mediaFields as $field) {
            $items = $this->data['data'][$field->name] ?? [];

            // Handle JSON string (from MediaSelector) or array
            if (is_string($items)) {
                $items = json_decode($items, true) ?: [];
            }

            // Ensure it's an array
            if (! is_array($items)) {
                $items = [];
            }

            foreach ($items as $item) {
                if (! is_array($item) || ! isset($item['id'])) {
                    continue;
                }

                $mediaAttachments->push([
                    'id' => $item['id'],
                    'properties' => ['field' => $field->name],
                ]);
            }
        }

        // Repeater media fields
        foreach ($this->repeaterFields as $repeaterField) {
            $mediaFieldNames = collect($repeaterField->items)
                ->where('type', 'media')
                ->pluck('name');

            $rows = $this->data['data'][$repeaterField->name] ?? [];
            foreach ($rows as $index => $row) {
                foreach ($mediaFieldNames as $mediaFieldName) {
                    $items = $row[$mediaFieldName] ?? [];

                    // Handle JSON string or array
                    if (is_string($items)) {
                        $items = json_decode($items, true) ?: [];
                    }

                    // Ensure it's an array
                    if (! is_array($items)) {
                        $items = [];
                    }

                    foreach ($items as $item) {
                        if (! is_array($item) || ! isset($item['id'])) {
                            continue;
                        }

                        $mediaAttachments->push([
                            'id' => $item['id'],
                            'properties' => [
                                'field' => "{$repeaterField->name}.{$index}.{$mediaFieldName}",
                            ],
                        ]);
                    }
                }
            }
        }

        // Batch sync
        $component->media()->detach();
        $mediaAttachments->each(fn ($item) => $component->media()->attach($item['id'], ['properties' => $item['properties']])
        );
    }
}
