<?php

declare(strict_types=1);

namespace FleekDash\Admin\Menu;

class MenuRoleProjectionService
{
    /**
     * @param array<int, array<string, mixed>> $items
     * @param array<string, bool> $capabilities
     * @return array<int, array<string, mixed>>
     */
    public function filterByCapabilities(array $items, array $capabilities): array
    {
        $result = [];
        foreach ($items as $item) {
            if (!\is_array($item) || !$this->canSeeItem($item, $capabilities)) {
                continue;
            }

            $item = $this->filterChildren($item, $capabilities);
            $result[] = $item;
        }

        return $result;
    }

    /**
     * @param array<int, array<string, mixed>> $items
     * @param array<string, bool> $capabilities
     * @return array<int, array<string, mixed>>
     */
    public function flagDeniedItems(array $items, array $capabilities): array
    {
        $result = [];
        foreach ($items as $item) {
            if (!\is_array($item)) {
                continue;
            }

            $result[] = $this->flagItemCapabilityState($item, $capabilities);
        }

        return $result;
    }

    /**
     * @param array<int, array<string, mixed>> $items
     * @param array<int, array<string, mixed>> $routes
     * @param array<string, bool> $capabilities
     * @param callable(array<string,mixed>):array<string,mixed> $convertRoute
     * @param callable(array<int,array<string,mixed>>):array<int,array<string,mixed>> $sortByPosition
     * @return array<int, array<string, mixed>>
     */
    public function appendAllowedIntegrationRoutes(
        array $items,
        array $routes,
        array $capabilities,
        callable $convertRoute,
        callable $sortByPosition
    ): array {
        if (empty($routes)) {
            return $items;
        }

        $integrationItems = [];
        foreach ($routes as $route) {
            if (!$this->isStandaloneRoute($route) || !$this->routeAllowed($route, $capabilities)) {
                continue;
            }
            $integrationItems[] = $convertRoute($route);
        }

        return $sortByPosition(array_merge($items, $integrationItems));
    }

    /** @param array<string, mixed> $item @param array<string, bool> $capabilities */
    private function canSeeItem(array $item, array $capabilities): bool
    {
        $cap = isset($item['capability']) && \is_string($item['capability']) ? $item['capability'] : '';
        return $cap === '' || (isset($capabilities[$cap]) && $capabilities[$cap] === true);
    }

    /** @param array<string, mixed> $item @param array<string, bool> $capabilities @return array<string,mixed> */
    private function flagItemCapabilityState(array $item, array $capabilities): array
    {
        $capability = isset($item['capability']) && \is_string($item['capability']) ? $item['capability'] : '';
        $requiresCapability = $capability !== '';
        $hasRoleCapability = !$requiresCapability || (isset($capabilities[$capability]) && $capabilities[$capability] === true);

        $item['requiresCapability'] = $requiresCapability;
        $item['hasRoleCapability'] = $hasRoleCapability;
        $item['capabilityDenied'] = $requiresCapability && !$hasRoleCapability;

        if (isset($item['submenu']) && \is_array($item['submenu'])) {
            $item['submenu'] = $this->flagDeniedItems($item['submenu'], $capabilities);
        }
        if (isset($item['children']) && \is_array($item['children'])) {
            $item['children'] = $this->flagDeniedItems($item['children'], $capabilities);
        }

        return $item;
    }

    /** @param array<string, mixed> $item @param array<string, bool> $capabilities @return array<string,mixed> */
    private function filterChildren(array $item, array $capabilities): array
    {
        if (isset($item['submenu']) && \is_array($item['submenu'])) {
            $item['submenu'] = $this->filterByCapabilities($item['submenu'], $capabilities);
        }
        if (isset($item['children']) && \is_array($item['children'])) {
            $item['children'] = $this->filterByCapabilities($item['children'], $capabilities);
        }
        return $item;
    }

    /** @param array<string,mixed> $route */
    private function isStandaloneRoute(array $route): bool
    {
        return ($route['integration_type'] ?? '') === 'standalone';
    }

    /** @param array<string,mixed> $route @param array<string, bool> $capabilities */
    private function routeAllowed(array $route, array $capabilities): bool
    {
        $permissions = isset($route['permissions']) && \is_array($route['permissions'])
            ? $route['permissions']
            : ['authenticated'];

        foreach ($permissions as $perm) {
            if (!\is_string($perm) || $perm === 'public' || $perm === 'authenticated') {
                continue;
            }
            if (!isset($capabilities[$perm]) || $capabilities[$perm] !== true) {
                return false;
            }
        }

        return true;
    }
}

