<?php

namespace App;

use App\Contracts\AbleToSyncContentElements;
use App\Contracts\InteractsWithContentSync;
use Illuminate\Support\Collection;
use Mtc\ContentManager\Contracts\ContentElement as ContentElementContract;
use Mtc\ContentManager\Contracts\ModelWithContent;
use Mtc\ContentManager\Contracts\TemplateElement;
use Mtc\ContentManager\TemplateRepository as ParentTemplateRepository;
use Mtc\MercuryDataModels\ContentElement;
use Mtc\MercuryDataModels\GlobalContent;
use Mtc\MercuryDataModels\Template;

class TemplateRepository extends ParentTemplateRepository implements InteractsWithContentSync, AbleToSyncContentElements
{
    public function exportToRemote(array $selections): array
    {
        return Template::query()
            ->with([
                'elements.element',
                'elements.globalContent',
            ])
            ->whereIn('id', $selections)
            ->get()
            ->map(function (Template $template) {
                $data = $template->toArray();
                $data['elements'] = $template->elements
                    ->map(function (TemplateElement $field) {
                        $data = $field->toArray();
                        if ($data['element_id']) {
                            $data['element_id'] = $field->element?->slug;
                            unset($data['element']);
                        }
                        if ($data['global_content_id']) {
                            $data['global_content_id'] = $field->globalContent?->slug;
                            unset($data['global_content']);
                        }
                        return $data;
                    });
                return $data;
            })
            ->toArray();
    }

    public function importRecord(array $entry): bool
    {
        /** @var Template $template */
        $template = Template::query()->create($entry);
        if (!empty($entry['elements'])) {
            collect($entry['elements'])
                ->each(function ($field) use ($template) {
                    if (!empty($field['element_id'])) {
                        $field['element_id'] = ContentElement::query()
                            ->where('slug', $field['element_id'])
                            ->firstOrFail()
                            ?->id;
                    }
                    if (!empty($field['global_content_id'])) {
                        $field['global_content_id'] = GlobalContent::query()
                            ->where('slug', $field['global_content_id'])
                            ->firstOrFail()
                            ?->id;
                    }
                    $template->elements()->create($field);
                });
        }

        return $template->exists;
    }

    public function isContentElementInSync(ModelWithContent $model, ContentElementContract $element): bool
    {
        $templateElements = $model->elements()->where('element_id', $element->id)->get();

        // Find non-matching elements by data column
        return $templateElements->reject(fn(TemplateElement $content) => collect($element->data)
                ->reject(fn($data, $key) => $data == ($content->data[$key] ?? null))
                ->isNotEmpty())
            // Find non-matching sub-elements/fields
            ->reject(fn(TemplateElement $content) => $element->fields
                // take out element that has at least one missing field
                ->filter(fn($field) => $content->subContent?->where('slug', $field->slug)->isEmpty())
                ->isNotEmpty())
            // in sync if element count matches all elements
            ->count() == $templateElements->count();
    }

    public function canBeImported(array $entry): bool
    {
        return collect($entry['elements'])
            ->filter(fn($element) => $this->unknownContentElement($element) || $this->unknownGlobalContent($element))
            ->isEmpty();
    }

    public function checkImportEntryValidity(array $dataEntry, array $allEntries): array
    {
        $errors = [];
        if (empty($dataEntry['slug'])) {
            $errors[] = __('validation.import_slug_missing', ['slug' => $dataEntry['slug']]);
        } elseif (Template::query()->where('slug', $dataEntry['slug'])->exists()) {
            $errors[] = __('validation.import_slug_taken', ['slug' => $dataEntry['slug']]);
        }
        $missingElements = $this->missingElements($dataEntry['elements'], $allEntries);
        $missingGlobalContent = $this->missingGlobalContent($dataEntry['elements']);
        foreach ($missingElements as $element) {
            $errors[] = __('validation.import_element_missing', [
                'slug' => $element['element_id'],
                'name' => $element['name']
            ]);
        }
        foreach ($missingGlobalContent as $content) {
            $errors[] = __('validation.import_global_content_missing', [
                'slug' => $content['global_content_id'],
                'name' => $content['name']
            ]);
        }
        return [
            'data' => $dataEntry,
            'errors' => $errors,
        ];
    }

    private function missingElements(Collection|array $fields, array $syncedEntries): Collection
    {
        /** @var Collection $elements */
        $syncedEntrySlugs = collect($syncedEntries)->pluck('slug');
        $elements = collect($fields)->pluck('element_id')->filter();
        $existing = ContentElement::query()->whereIn('slug', $elements)->pluck('slug');
        $missing = $elements->diff($existing)
            ->reject(fn($missing) => $syncedEntrySlugs->search($missing) !== false);
        return collect($fields)
            ->filter(fn($field) => isset($field['element_id']) && $missing->search($field['element_id']) !== false);
    }

    private function missingGlobalContent(Collection|array $fields): Collection
    {
        $elements = collect($fields)->pluck('global_content_id')->filter();
        $existing = GlobalContent::query()->whereIn('slug', $elements)->pluck('slug');
        $missing = $elements->diff($existing);
        return collect($fields)
            ->filter(fn($field) => isset($field['global_content_id'])
                && $missing->search($field['global_content_id']) !== false);
    }

    protected function fieldDataValues($field): array
    {
        $except = [];
        if ($this->hasCustomFieldUI($field) === false) {
            $except = [
                'componentName',
                'component',
                'icon',
            ];
        }
        return collect(parent::fieldDataValues($field))
            ->except([
                'fieldId',
                'error',
                'optionsMenuVisible',
            ])
            ->except($except)
            ->toArray();
    }

    private function hasCustomFieldUI(array $field): bool
    {
        if ($field['field_type'] === 'element') {
            return true;
        }

        // TODO: determine if value differs from primitive
        return false;
    }

    private function unknownContentElement(Collection|array $field): bool
    {
        return isset($field['element_id'])
            && ContentElement::query()
            ->where('slug', $field['element_id'])
            ->exists() === false;
    }


    private function unknownGlobalContent(Collection|array $element): bool
    {
        return isset($element['global_content_id'])
            && GlobalContent::query()
            ->where('slug', $element['global_content_id'])
            ->exists() === false;
    }

    public function syncElementWithTemplate(int $entry_id, ?ContentElementContract $element = null): void
    {
        // TODO: Implement syncContentWithTemplate() method.
    }
}
