<?php

namespace App;

use App\Contracts\InteractsWithContentSync;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Mtc\ContentManager\Contracts\Menu;
use Mtc\ContentManager\Facades\Media;
use Mtc\MercuryDataModels\ContentElement;
use Mtc\MercuryDataModels\Menu as MenuModel;
use Mtc\MercuryDataModels\MenuEntry;

class MenuRepository extends \Mtc\ContentManager\MenuRepository implements InteractsWithContentSync
{
    use ValidatesRequests;

    protected Collection $entries;

    protected Collection $newMenuEntryIds;

    #[\Override]
    public function create(Menu $menu, Request $request)
    {
        Cache::forget(tenant('id') . '-menus');
        return parent::create($menu, $request);
    }

    #[\Override]
    public function update(Menu $menu, Request $request)
    {
        $this->validate($request, [
            'root_entries.*.children.*.children.*.children.*.children' => 'prohibited',
        ], [], [
            'root_entries.*.children.*.children.*.children.*.children' => '5th level menu entry'
        ]);
        if (config('pages.use_transactions')) {
            DB::beginTransaction();
        }
        $menu->fill($request->input())->save();

        $this->entries = $this->buildCollection($request->input('root_entries', []));
        $this->syncEntries($menu);
        if (config('pages.use_transactions')) {
            DB::commit();
        }

        Cache::forget(tenant('id') . '-menus');
        Cache::forget(tenant('id') . '-get-site');
        return $menu;
    }

    protected function buildCollection(array $input)
    {
        return collect($input)
            ->map(function ($entry) {
                if (isset($entry['children'])) {
                    $entry['children'] = $this->buildCollection($entry['children']);
                }
                return $entry;
            });
    }

    protected function syncEntries(Menu $menu)
    {
        $entries = collect();
        $this->newMenuEntryIds = collect([]);
        $flatTree = $this->flattenInput($entries, $this->entries, null);
        $requestEntryIds = $flatTree->pluck('id')->toArray();
        $currentEntries = $menu->entries()->get();
        $currentEntries->reject(fn($entry) => in_array($entry->id, $requestEntryIds))
            ->each(fn($entry) => $entry->delete());
        $this->entries->each(fn($entry, $index) => $this->recusrsiveEntrySave($menu, $entry, $index, null));
    }

    private function recusrsiveEntrySave(Menu $menu, $entryData, int $order, ?int $parentId)
    {
        $entryData['menu_id'] = $menu->id;
        $entryData['parent_id'] = $parentId;
        $entryData['_lft'] = $order;
        $entryData['data'] = $entryData['custom_data'] ?? null;
        if ($parentId !== null) {
            $entryData['content'] = [];
        }

        $entry = $menu->entries()
            ->updateOrCreate([
                'id' => $entryData['id'],
            ], $entryData);

        if (empty($parentId) && isset($entryData['media'])) {
            Media::setUsesForModel($entryData['media'], $entry);
        }

        collect($entryData['children'] ?? [])
            ->each(fn($childEntry, $index) => $this->recusrsiveEntrySave($menu, $childEntry, $index, $entry->id));
    }

    protected function flattenInput(Collection $entries, Collection $input, $parent_id): Collection
    {
        $input->map(function ($entry) use ($entries, $parent_id) {
            $entry['parent_id'] = $parent_id;
            $entries->push($entry);

            if (isset($entry['children']) && $entry['children']->isNotEmpty()) {
                $this->flattenInput($entries, $entry['children'], $entry['id']);
            }
            return $entry;
        });

        return $entries;
    }

    public function importRecord(array $entry): bool
    {
        /** @var Menu $menu */
        $entry['first_child_element_id'] = ContentElement::query()
            ->where('slug', $entry['first_child_element_id'])
            ->first()
            ?->id;
        $menu = MenuModel::query()->create($entry);
        if (!empty($entry['entries'])) {
            collect($entry['entries'])
                ->map(function ($field, $index) use ($menu) {
                    $field['_lft'] = $index;
                    if (!empty($field['element_id'])) {
                        $field['element_id'] = ContentElement::query()
                            ->where('slug', $field['element_id'])
                            ->firstOrFail()
                            ?->id;
                    }
                    $entry = $menu->entries()->create($field);
                    $field['new_id'] = $entry->id;
                    return $field;
                })
                ->each(fn($entry) => $menu->entries()
                    ->where('parent_id', $entry['id'])
                    ->update(['parent_id' => $entry['new_id']]));
        }

        return $menu->exists;
    }

    public function canBeImported(array $entry): bool
    {
        return empty($entry['first_child_element_id']) || ContentElement::query()
                ->where('slug', $entry['first_child_element_id'])
                ->exists();
    }

    public function exportToRemote(array $selections): array
    {
        return MenuModel::query()->newQuery()
            ->with([
                'element',
                'entries.element',
            ])
            ->whereIn('id', $selections)
            ->get()
            ->map(function (Menu $element) {
                $data = $element->toArray();
                if ($data['first_child_element_id']) {
                    $data['first_child_element_id'] = $element->element?->slug;
                    unset($data['element']);
                }
                $data['entries'] = $element->entries
                    ->map(function (MenuEntry $field) {
                        $data = $field->toArray();
                        if ($data['element_id']) {
                            $data['element_id'] = $field->element?->slug;
                            unset($data['element']);
                        }
                        return $data;
                    });
                return $data;
            })
            ->toArray();
    }

    public function checkImportEntryValidity(array $dataEntry, array $allEntries): array
    {
        $errors = [];
        if (empty($dataEntry['slug'])) {
            $errors[] = __('validation.import_slug_missing', ['slug' => $dataEntry['slug']]);
        } elseif (MenuModel::query()->where('slug', $dataEntry['slug'])->exists()) {
            $errors[] = __('validation.import_slug_taken', ['slug' => $dataEntry['slug']]);
        }

        if (!empty($dataEntry['first_child_element_id'])) {
            $missingElement = ContentElement::query()
                ->where('slug', $dataEntry['first_child_element_id'])
                ->exists() === false;
        }
        if (!empty($missingElement)) {
            $errors[] = __('validation.import_element_missing', [
                'slug' => $dataEntry['first_child_element_id'],
                'name' => $dataEntry['title']
            ]);
        }
        return [
            'data' => $dataEntry,
            'errors' => $errors,
        ];
    }
}
