<?php

namespace MintyPHP\Repository\Support;

/** Static helpers for safe SQL query building: LIKE escaping, limit/offset clamping, ID list normalization, and enum filtering. */
class RepoQuery
{
    private static function normalizeLikeValue(string $value): string
    {
        $value = trim($value);
        if ($value === '') {
            return '';
        }
        $value = str_replace('\\', '\\\\', $value);
        $value = str_replace('_', '\\_', $value);
        $value = str_replace('*', '%', $value);
        if (!str_starts_with($value, '%')) {
            $value = '%' . $value;
        }
        if (!str_ends_with($value, '%')) {
            $value = $value . '%';
        }
        return $value;
    }
    public static function sanitizeLimitOffset(
        array $options,
        int $defaultLimit = 10,
        int $minLimit = 1,
        int $maxLimit = 100,
        int $defaultOffset = 0
    ): array {
        $limit = (int) ($options['limit'] ?? $defaultLimit);
        if ($limit < $minLimit) {
            $limit = $defaultLimit;
        } elseif ($limit > $maxLimit) {
            $limit = $maxLimit;
        }

        $offset = (int) ($options['offset'] ?? $defaultOffset);
        if ($offset < 0) {
            $offset = 0;
        }

        return [$limit, $offset];
    }

    public static function sanitizeOrder(
        array $options,
        array $allowedOrder,
        string $defaultOrder = 'id',
        string $defaultDir = 'desc'
    ): array {
        $order = (string) ($options['order'] ?? $defaultOrder);
        $dir = strtolower((string) ($options['dir'] ?? $defaultDir));
        if (!in_array($order, $allowedOrder, true)) {
            $order = $defaultOrder;
        }
        if (!in_array($dir, ['asc', 'desc'], true)) {
            $dir = $defaultDir;
        }
        return [$order, $dir];
    }

    public static function addLikeFilter(array &$where, array &$params, array $fields, string $value): void
    {
        $like = self::normalizeLikeValue($value);
        if ($like === '') {
            return;
        }
        $clauses = [];
        foreach ($fields as $field) {
            $clauses[] = $field . " like ? escape '\\\\'";
            $params[] = $like;
        }
        if ($clauses) {
            $where[] = '(' . implode(' or ', $clauses) . ')';
        }
    }

    public static function addEnumFilter(array &$where, array &$params, $value, array $map): void
    {
        if ($value === null) {
            return;
        }
        $normalized = strtolower(trim((string) $value));
        if ($normalized === '' || $normalized === 'all') {
            return;
        }
        foreach ($map as $entry) {
            $aliases = $entry['aliases'] ?? [];
            $aliases = array_map('strtolower', $aliases);
            if (!in_array($normalized, $aliases, true)) {
                continue;
            }
            $sql = (string) ($entry['sql'] ?? '');
            if ($sql === '') {
                return;
            }
            $where[] = $sql;
            $paramsToAdd = $entry['params'] ?? [];
            foreach ($paramsToAdd as $param) {
                $params[] = $param;
            }
            return;
        }
    }

    public static function uuidV4(): string
    {
        $data = random_bytes(16);
        $data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
        $data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
    }

    public static function addEqualsFilter(array &$where, array &$params, $value, string $sql, bool $allowEmpty = false): void
    {
        // Use for simple "= ?" filters; skip when empty to keep WHERE clean.
        if ($value === null) {
            return;
        }
        $normalized = trim((string) $value);
        if ($normalized === '' && !$allowEmpty) {
            return;
        }
        $where[] = $sql;
        $params[] = $normalized;
    }

    public static function normalizeIdList($value): array
    {
        if (is_string($value)) {
            $value = array_filter(array_map('trim', explode(',', $value)));
        } elseif (!is_array($value)) {
            return [];
        }
        $flat = [];
        array_walk_recursive($value, static function ($item) use (&$flat): void {
            $flat[] = $item;
        });
        $ids = [];
        foreach ($flat as $item) {
            $id = (int) trim((string) $item);
            if ($id > 0) {
                $ids[] = $id;
            }
        }
        $ids = array_values(array_unique($ids));
        sort($ids, SORT_NUMERIC);
        return $ids;
    }

    public static function normalizeStringList(mixed $value, int $max = 200, ?callable $sanitizer = null): array
    {
        if ($max <= 0) {
            return [];
        }

        if (is_string($value)) {
            $value = array_filter(array_map('trim', explode(',', $value)));
        } elseif (!is_array($value)) {
            return [];
        }

        $flat = [];
        array_walk_recursive($value, static function ($item) use (&$flat): void {
            $flat[] = $item;
        });

        $items = [];
        $seen = [];
        foreach ($flat as $item) {
            $text = trim((string) $item);
            if ($text === '') {
                continue;
            }

            if ($sanitizer !== null) {
                $text = trim((string) $sanitizer($text));
                if ($text === '') {
                    continue;
                }
            }

            if (isset($seen[$text])) {
                continue;
            }

            $seen[$text] = true;
            $items[] = $text;
            if (count($items) >= $max) {
                break;
            }
        }

        return $items;
    }
}
