<?php

declare(strict_types=1);

namespace FleekDash\API;

use FleekDash\Admin\Menu\CoreMenuRestoreService;
use FleekDash\Admin\Menu\MenuItemFormatter;
use FleekDash\Admin\Menu\RouteConverter;
use FleekDash\Admin\Menu\RoutePermissionChecker;

class MenuCurrentUserPayloadService
{
    /** @var callable(array<int,array<string,mixed>>):array<int,array<string,mixed>> */
    private $sortByPosition;
    private CoreMenuRestoreService $coreMenuRestoreService;
    private MenuItemFormatter $menuItemFormatter;

    /**
     * @param callable(array<int,array<string,mixed>>):array<int,array<string,mixed>> $sortByPosition
     */
    public function __construct(
        callable $sortByPosition,
        ?MenuItemFormatter $menuItemFormatter = null,
    ) {
        $this->sortByPosition = $sortByPosition;
        $this->coreMenuRestoreService = new CoreMenuRestoreService();
        $this->menuItemFormatter = $menuItemFormatter ?? new MenuItemFormatter(new RouteConverter());
    }

    /**
     * @param array<int, mixed> $menu
     * @param array<string, mixed> $submenu
     * @param array<string, array<int, mixed>> $snapshotMenuRowBySlug Snapshot wp-admin rows keyed by slug for restored core labels
     * @return array<int, array<string, mixed>>
     */
    public function buildMenuItems(array $menu, array $submenu, array $snapshotMenuRowBySlug = []): array
    {
        $formatter = $this->menuItemFormatter;
        $menuItems = $formatter->formatMenuItems($menu, $submenu);
        $menuItems = $this->coreMenuRestoreService->restoreMissingTopLevelItems(
            $menuItems,
            static fn(string $capability): bool => $capability === '' || \current_user_can($capability),
            $submenu,
            $formatter,
            $snapshotMenuRowBySlug,
        );
        // After top-level restoration, fill empty submenus for native parents (edit.php, upload.php,
        // …) using WordPress-core baselines. Runs BEFORE applyBackwardCompatibilityPayload so the
        // legacy payload reflects the restored submenu tree.
        $menuItems = $this->coreMenuRestoreService->ensureNativeSubmenusPresent($menuItems, $formatter);
        $menuItems = MenuItemTransformHelper::applyBackwardCompatibilityPayload($menuItems);
        return $this->appendIntegrationRoutes($menuItems);
    }

    /**
     * Append integration routes to menu items
     *
     * @param array<int, array<string, mixed>> $items
     * @return array<int, array<string, mixed>>
     */
    private function appendIntegrationRoutes(array $items): array
    {
        if (!\class_exists(\FleekDash\Integration\RouteManager::class)) {
            return $items;
        }

        $routes = \FleekDash\Integration\RouteManager::getAll(true, true);
        if (empty($routes) || !\is_array($routes)) {
            return $items;
        }

        $integrationItems = $this->filterAndConvertIntegrationRoutes($routes);
        $integrationItems = $this->removeIntegrationDuplicatesAgainstExisting($items, $integrationItems);
        $merged = \array_merge($items, $integrationItems);
        $merged = $this->removeExactDuplicateTopLevelItems($merged);
        return ($this->sortByPosition)($merged);
    }

    /**
     * @param array<int, array<string, mixed>> $existingItems
     * @param array<int, array<string, mixed>> $integrationItems
     * @return array<int, array<string, mixed>>
     */
    private function removeIntegrationDuplicatesAgainstExisting(array $existingItems, array $integrationItems): array
    {
        [$existingHrefs, $existingUniqueIds] = $this->buildExistingMenuIndexes($existingItems);

        $filtered = [];
        foreach ($integrationItems as $item) {
            $href = $this->readStringField($item, 'href');
            $uniqueId = $this->readStringField($item, 'unique_id');
            $isDuplicate = $this->isDuplicateByHrefOrUniqueId($href, $uniqueId, $existingHrefs, $existingUniqueIds);
            if (!$isDuplicate) {
                $filtered[] = $item;
            }
        }

        return $filtered;
    }

    /**
     * @param array<int, array<string, mixed>> $items
     * @return array{0: array<string,bool>, 1: array<string,bool>}
     */
    private function buildExistingMenuIndexes(array $items): array
    {
        $hrefs = [];
        $uniqueIds = [];
        foreach ($items as $item) {
            $href = $this->readStringField($item, 'href');
            $uniqueId = $this->readStringField($item, 'unique_id');
            if ($href !== '') {
                $hrefs[$href] = true;
            }
            if ($uniqueId !== '') {
                $uniqueIds[$uniqueId] = true;
            }
        }
        return [$hrefs, $uniqueIds];
    }

    /** @param array<string,mixed> $item */
    private function readStringField(array $item, string $field): string
    {
        return isset($item[$field]) && \is_string($item[$field]) ? $item[$field] : '';
    }

    /**
     * @param array<string,bool> $existingHrefs
     * @param array<string,bool> $existingUniqueIds
     */
    private function isDuplicateByHrefOrUniqueId(
        string $href,
        string $uniqueId,
        array $existingHrefs,
        array $existingUniqueIds,
    ): bool {
        return ($href !== '' && isset($existingHrefs[$href]))
            || ($uniqueId !== '' && isset($existingUniqueIds[$uniqueId]));
    }

    /**
     * Keep full menu while removing exact duplicates only.
     *
     * @param array<int, array<string, mixed>> $items
     * @return array<int, array<string, mixed>>
     */
    private function removeExactDuplicateTopLevelItems(array $items): array
    {
        $seen = [];
        $result = [];
        foreach ($items as $item) {
            $uniqueId = $this->readStringField($item, 'unique_id');
            $href = $this->readStringField($item, 'href');
            $name = $this->readStringField($item, 'name');
            $position = isset($item['position']) ? (string) $item['position'] : '';
            $dedupeKey = $uniqueId !== ''
                ? "uid:{$uniqueId}"
                : "key:{$name}|{$href}|{$position}";
            if (!isset($seen[$dedupeKey])) {
                $seen[$dedupeKey] = true;
                $result[] = $item;
            }
        }
        return $result;
    }

    /** @return array<int, array<string, mixed>> */
    private function filterAndConvertIntegrationRoutes(array $routes): array
    {
        $result = [];
        foreach ($routes as $route) {
            if (($route['integration_type'] ?? '') !== 'standalone') {
                continue;
            }
            $permissions = $route['permissions'] ?? ['authenticated'];
            if (!RoutePermissionChecker::canCurrentUserAccessRoute($permissions)) {
                continue;
            }
            $result[] = MenuItemTransformHelper::convertIntegrationRouteToMenuItem($route);
        }
        return $result;
    }

}

