<?php

namespace Snaju\Inception\Util;

use ArrayAccess;
use Closure;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;

class ArrayUtil
{

    public static function isNumericKeys(array|object $arr)
    {
        foreach ($arr as $k => $v) {
            if (!is_numeric($k)) {
                return false;
            }
        }
        return true;
    }

    public static function toArray(&$arr, $path, $value, $separator = '.')
    {
        $keys = explode($separator, $path);

        foreach ($keys as $key) {
            $arr = &$arr[$key];
        }

        $arr = $value;
    }

    public static function &getRef(&$arr, $path, $separator = '.')
    {
        $keys = explode($separator, $path);

        foreach ($keys as $key) {
            $arr = &$arr[$key];
        }

        return $arr;
    }

    public static function pathExist(&$arr, $path, $separator = '.')
    {
        $keys = explode($separator, $path);

        foreach ($keys as $key) {
            if (!isset($arr[$key])) {
                return false;
            }
            $arr = &$arr[$key];
        }

        return true;
    }

    public static function unsetKey(&$arr, string $dotSeparatedKey)
    {
        $keys = explode('.', $dotSeparatedKey);
        $pointer = &$arr;
        $current = false; // just to make code sniffer happy
        // we traverse all but the last key
        while (($current = array_shift($keys)) && (count($keys) > 0)) {
            // if some key is missing all the subkeys will be already unset
            if (!array_key_exists($current, $pointer)) {
                // is already unset somewhere along the way
                return;
            }
            // set pointer to new, deeper level
            // called for all but last key
            $pointer = &$pointer[$current];
        }
        // handles empty input string
        if ($current) {
            // we finally unset what we wanted
            unset($pointer[$current]);
        }
    }

    public static function fromArray($array, $key, $default = null)
    {
        if (!static::accessible($array)) {
            return value($default);
        }
        if (is_null($key)) {
            return $array;
        }
        if (static::exists($array, $key)) {
            return $array[$key];
        }
        if (strpos($key, '.') === false) {
            return $array[$key] ?? value($default);
        }
        foreach (explode('.', $key) as $segment) {
            if (static::accessible($array) && static::exists($array, $segment)) {
                $array = $array[$segment];
            } else {
                return value($default);
            }
        }
        return $array;
    }

//    public static function getDotArray($array)
//    {
//        $ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
//        $result = array();
//        foreach ($ritit as $leafValue) {
//            $keys = array();
//            foreach (range(0, $ritit->getDepth()) as $depth) {
//                $keys[] = $ritit->getSubIterator($depth)->key();
//            }
//
//            $result[implode('.', $keys)] = $leafValue;
//        }
//
//        return $result;
//    }

    public static function getDotArray($array)
    {
        return array_dot($array);
    }

    public static function accessible($value)
    {
        return is_array($value) || $value instanceof ArrayAccess;
    }

    public static function exists($array, $key)
    {
        if ($array instanceof ArrayAccess) {
            return $array->offsetExists($key);
        }
        return array_key_exists($key, $array);
    }

    public static function filterKeys($array, $keys): array
    {
        $newArray = [];
        foreach ($array as $k => $v) {
            if (in_array($k, $keys)) {
                $newArray[$k] = $v;
            }
        }

        return $newArray;
    }

}

if (!function_exists('value')) {
    /**
     * Return the default value of the given value.
     *
     * @param mixed $value
     * @return mixed
     */
    function value($value)
    {
        return $value instanceof Closure ? $value() : $value;
    }
}