<?php

namespace App\Domain\Website\Services;

use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Website\Models\WebsiteMenu;
use App\Domain\Website\Models\WebsiteMenuItem;
use Illuminate\Support\Facades\DB;

class WebsiteMenuService
{
    public const MAX_DEPTH = 3;

    public const KEYS = ['primary', 'footer', 'footer_secondary', 'mobile', 'utility'];

    public function getOrCreate(string $key): WebsiteMenu
    {
        if (! in_array($key, self::KEYS, true)) {
            throw new DomainException('نوع القائمة غير معروف.');
        }

        return WebsiteMenu::firstOrCreate(
            ['key' => $key],
            ['name' => WebsiteMenu::labelFor($key)],
        );
    }

    public function addItem(WebsiteMenu $menu, array $data, ?WebsiteMenuItem $parent = null): WebsiteMenuItem
    {
        if ($parent && $this->depthOf($parent) + 1 >= self::MAX_DEPTH) {
            throw new DomainException('تم بلوغ الحد الأقصى لتداخل القوائم.');
        }

        return DB::transaction(fn () => WebsiteMenuItem::create([
            'website_menu_id' => $menu->id,
            'parent_id' => $parent?->id,
            'label' => $data['label'] ?? null,
            'label_en' => $data['label_en'] ?? null,
            'link_type' => $data['link_type'] ?? 'page',
            'website_page_id' => $data['website_page_id'] ?? null,
            'url' => $data['url'] ?? null,
            'anchor' => $data['anchor'] ?? null,
            'route_name' => $data['route_name'] ?? null,
            'icon' => $data['icon'] ?? null,
            'open_in_new_tab' => (bool) ($data['open_in_new_tab'] ?? false),
            'is_visible' => (bool) ($data['is_visible'] ?? true),
            'highlight' => (bool) ($data['highlight'] ?? false),
            'sort_order' => (int) (WebsiteMenuItem::where('website_menu_id', $menu->id)
                ->where('parent_id', $parent?->id)->max('sort_order') + 1),
        ]));
    }

    public function updateItem(WebsiteMenuItem $item, array $data): WebsiteMenuItem
    {
        return DB::transaction(function () use ($item, $data) {
            // Clear the target fields that no longer apply, so a type change
            // cannot leave a stale url behind that later resurfaces.
            $type = $data['link_type'] ?? $item->link_type;
            $data['website_page_id'] = $type === 'page' ? ($data['website_page_id'] ?? null) : null;
            $data['url'] = $type === 'url' ? ($data['url'] ?? null) : null;
            $data['anchor'] = $type === 'anchor' ? ($data['anchor'] ?? null) : null;
            $data['route_name'] = $type === 'route' ? ($data['route_name'] ?? null) : null;

            $item->update($data);

            return $item->refresh();
        });
    }

    public function deleteItem(WebsiteMenuItem $item): void
    {
        DB::transaction(fn () => $item->delete());
    }

    public function move(WebsiteMenuItem $item, int $direction): void
    {
        $siblings = WebsiteMenuItem::where('website_menu_id', $item->website_menu_id)
            ->where('parent_id', $item->parent_id)
            ->orderBy('sort_order')->orderBy('id')
            ->pluck('id')->all();

        $i = array_search($item->id, $siblings, true);
        $target = $i + $direction;

        if ($i === false || ! isset($siblings[$target])) {
            return;
        }

        [$siblings[$i], $siblings[$target]] = [$siblings[$target], $siblings[$i]];
        $this->reorder($siblings);
    }

    public function reorder(array $orderedIds): void
    {
        DB::transaction(function () use ($orderedIds) {
            foreach ($orderedIds as $i => $id) {
                WebsiteMenuItem::where('id', $id)->update(['sort_order' => $i]);
            }
        });
    }

    /** Visible tree for public rendering. */
    public function tree(string $key)
    {
        return $this->getOrCreate($key)
            ->items()
            ->visible()
            ->with(['childrenRecursive', 'page'])
            ->get();
    }

    private function depthOf(WebsiteMenuItem $item): int
    {
        $depth = 0;
        $cursor = $item;

        while ($cursor->parent_id && $depth < self::MAX_DEPTH + 1) {
            $cursor = $cursor->parent()->first();
            if (! $cursor) {
                break;
            }
            $depth++;
        }

        return $depth;
    }
}
