<?php

namespace Fuse\Core;

use Fuse\Core\Register;
use Fuse\Exception\LogicalSearchInvalidQueryForKeyException;
use Fuse\Tools\KeyStore;

use function Fuse\Helpers\Types\{isArray, isAssoc};

function isExpression(array $query): bool
{
    return isset($query[LogicalOperator::AND]) || isset($query[LogicalOperator::OR]);
}

function isPath(array $query): bool
{
    return isset($query[KeyType::PATH]);
}

function isLeaf(array $query): bool
{
    return isAssoc($query) && !isExpression($query);
}

/**
 * @return array[][]
 *
 * @psalm-return array{$and: list<array>}
 */
function convertToExplicit(array $query): array
{
    return [
        LogicalOperator::AND => array_map(fn($key) => [$key => $query[$key]], array_keys($query)),
    ];
}

function parseNext(array $query, array $options, bool $auto): array
{
    $keys = array_keys($query);

    $isQueryPath = isPath($query);

    if (!$isQueryPath && sizeof($keys) > 1 && !isExpression($query)) {
        return parseNext(convertToExplicit($query), $options, $auto);
    }

    if (isLeaf($query)) {
        $key = $isQueryPath ? $query[KeyType::PATH] : $keys[0];

        $pattern = $isQueryPath ? $query[KeyType::PATTERN] : $query[$key];

        if (!is_string($pattern)) {
            throw new LogicalSearchInvalidQueryForKeyException($key);
        }

        $obj = [
            'keyId' => KeyStore::createKeyId($key),
            'pattern' => $pattern,
        ];

        if ($auto) {
            $obj['searcher'] = Register::createSearcher($pattern, $options);
        }

        return $obj;
    }

    $node = [
        'children' => [],
        'operator' => $keys[0],
    ];

    foreach ($keys as $key) {
        $value = $query[$key];

        if (isArray($value)) {
            foreach ($value as $item) {
                $node['children'][] = parseNext($item, $options, $auto);
            }
        }
    }

    return $node;
}

// When `auto` is `true`, the parse function will infer and initialize and add
// the appropriate `Searcher` instance
function parse(array $query, array $options, array $additionalOptions = []): array
{
    $auto = $additionalOptions['auto'] ?? true;

    if (!isExpression($query)) {
        $query = convertToExplicit($query);
    }

    return parseNext($query, $options, $auto);
}
