<?php

declare(strict_types=1);

namespace FleekDash\Admin\Menu;

class MenuOverridesApplier
{
    /**
     * @param array<int, array<string, mixed>> $items
     * @param array<string, mixed> $overrides
     * @return array<int, array<string, mixed>>
     */
    public function apply(array $items, array $overrides): array
    {
        if (empty($overrides)) {
            return $items;
        }

        $decorated = [];
        $index = 0;
        foreach ($items as $item) {
            if (!\is_array($item)) {
                continue;
            }

            $merged = $this->mergeItemOverride($item, $overrides);
            if ($merged === null) {
                $index++;
                continue;
            }

            if (isset($merged['submenu']) && \is_array($merged['submenu'])) {
                $merged['submenu'] = $this->apply($merged['submenu'], $overrides);
            }
            if (isset($merged['children']) && \is_array($merged['children'])) {
                $merged['children'] = $this->apply($merged['children'], $overrides);
            }

            $merged['__sort_index'] = $index;
            $decorated[] = $merged;
            $index++;
        }

        usort($decorated, [$this, 'compareItems']);

        foreach ($decorated as &$item) {
            unset($item['__sort_index']);
        }

        return $decorated;
    }

    /** @param array<string,mixed> $item @param array<string,mixed> $overrides @return array<string,mixed>|null */
    private function mergeItemOverride(array $item, array $overrides): ?array
    {
        $itemId = $this->resolveItemId($item);
        $override = ($itemId !== '' && isset($overrides[$itemId]) && \is_array($overrides[$itemId]))
            ? $overrides[$itemId]
            : null;

        if ($override === null) {
            return $item;
        }

        if (isset($override['visible']) && $override['visible'] === false) {
            return null;
        }

        if (isset($override['name']) && \is_string($override['name'])) {
            $item['name'] = $override['name'];
        }
        if (isset($override['title']) && \is_string($override['title'])) {
            $item['title'] = $override['title'];
        }
        if (isset($override['order']) && \is_numeric($override['order'])) {
            $item['order'] = (float) $override['order'];
        }
        if (isset($override['visible'])) {
            $item['visible'] = $override['visible'] === true;
        }

        return $item;
    }

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

    /** @param array<string,mixed> $a @param array<string,mixed> $b */
    private function compareItems(array $a, array $b): int
    {
        $orderA = isset($a['order']) ? (float) $a['order'] : 0.0;
        $orderB = isset($b['order']) ? (float) $b['order'] : 0.0;
        if ($orderA === $orderB) {
            $idxA = isset($a['__sort_index']) ? (int) $a['__sort_index'] : 0;
            $idxB = isset($b['__sort_index']) ? (int) $b['__sort_index'] : 0;
            return $idxA <=> $idxB;
        }
        return $orderA <=> $orderB;
    }
}

