<?php

/**
 * Common Array Functions
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Common\Functions;

use phpseclib4\Exception\{InvalidStateException, UnexpectedValueException};

/**
 * Common Array Functions
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Arrays
{
    /**
     * Get a reference to a subarray
     *
     * This is kinda like Laravel's Arr::set() / Arr::get() method with their dot notation
     * except that / is used as the component separator instead
     */
    public static function &subArray(array|\ArrayAccess|null &$root, string $path, bool $create = false): array|\ArrayAccess|null
    {
        $false = null;

        if (!isset($root)) {
            // if you do "return false" you'll get this error:
            // Notice: Only variable references should be returned by reference
            return $false;
        }

        foreach (explode('/', $path) as $i) {
            if (!isset($root)) {
                return $false;
            }

            if (!is_array($root) && !$root instanceof \ArrayAccess) {
                return $false;
            }

            if (!isset($root[$i])) {
                if (!$create) {
                    return $false;
                }

                $root[$i] = [];
            }

            $root = &$root[$i];
        }

        return $root;
    }

    public static function &subArrayWithWildcards(array|\ArrayAccess &$root, string $path, bool $create = false): mixed
    {
        $parts = explode('/', $path);
        foreach ($parts as $k => $i) {
            if (!isset($root)) {
                $loc = implode('/', array_slice($parts, 0, $k));
                throw new UnexpectedValueException("Unable to find node for $loc");
            }

            if (!is_array($root) && !$root instanceof \ArrayAccess) {
                $loc = implode('/', array_slice($parts, 0, $k));
                throw new UnexpectedValueException("$loc isn't an array or an instance of ArrayAccess");
            }

            if ($i == '*') {
                $path = implode('/', array_slice($parts, $k + 1));
                foreach ($root as $key => $val) {
                    if (empty($path)) {
                        return $val;
                    } else {
                        $val = &self::subArrayWithWildcards($root[$key], $path, $create);
                        return $val;
                    }
                }
                $loc = implode('/', array_slice($parts, 0, $k));
                throw new UnexpectedValueException("$loc wasn't found");
            }

            if (!isset($root[$i])) {
                if (!$create) {
                    $loc = implode('/', array_slice($parts, 0, $k));
                    throw new UnexpectedValueException("$loc wasn't found and the create flag wasn't set");
                }

                $root[$i] = [];
            }

            $root = &$root[$i];
        }

        return $root;
    }

    public static function subArrayMapWithWildcards(array|\ArrayAccess &$root, string $path, \Closure $func): void
    {
        $parts = explode('/', $path);
        foreach ($parts as $k => $i) {
            if (!is_array($root) && !$root instanceof \ArrayAccess) {
                return;
            }

            if ($i == '*') {
                $path = implode('/', array_slice($parts, $k + 1));
                foreach ($root as $key => $val) {
                    if (empty($path)) {
                        $root[$key] = $func($val);
                    } elseif (is_array($root[$key]) || $root[$key] instanceof \ArrayAccess) {
                        self::subArrayMapWithWildcards($root[$key], $path, $func);
                    }
                }
                return;
            }

            if (!isset($root[$i])) {
                return;
            }

            if ($k == count($parts) - 1) {
                $root[$i] = $func($root[$i]);
                return;
            }

            $root = &$root[$i];
        }

        throw new InvalidStateException('Reached supposedly unreachable section of code');
    }
}
