<?php

declare(strict_types=1);

namespace App\Framework\Cache\Driver;

use App\Framework\Cache\CacheDriver;
use App\Framework\Cache\CacheItem;
use App\Framework\Cache\CacheKey;
use App\Framework\Cache\CacheResult;
use App\Framework\Cache\Contracts\Scannable;

final class InMemoryCache implements CacheDriver, Scannable
{
    private array $data = [];

    public function get(CacheKey ...$keys): CacheResult
    {
        if (empty($keys)) {
            return CacheResult::empty();
        }

        $items = [];
        foreach ($keys as $key) {
            $keyString = (string)$key;
            if (isset($this->data[$keyString])) {
                $items[] = CacheItem::hit($key, $this->data[$keyString]);
            } else {
                $items[] = CacheItem::miss($key);
            }
        }

        return CacheResult::fromItems(...$items);
    }

    public function set(CacheItem ...$items): bool
    {
        if (empty($items)) {
            return true;
        }

        foreach ($items as $item) {
            $keyString = (string)$item->key;
            $this->data[$keyString] = $item->value;
        }

        return true;
    }

    public function has(CacheKey ...$keys): array
    {
        if (empty($keys)) {
            return [];
        }

        $results = [];
        foreach ($keys as $key) {
            $keyString = (string)$key;
            $results[$keyString] = isset($this->data[$keyString]);
        }

        return $results;
    }

    public function forget(CacheKey ...$keys): bool
    {
        if (empty($keys)) {
            return true;
        }

        foreach ($keys as $key) {
            $keyString = (string)$key;
            unset($this->data[$keyString]);
        }

        return true;
    }

    public function clear(): bool
    {
        $this->data = [];

        return true;
    }

    // === Scannable Interface Implementation ===

    public function scan(string $pattern, int $limit = 1000): array
    {
        $regex = $this->patternToRegex($pattern);
        $matches = [];
        $count = 0;

        foreach (array_keys($this->data) as $key) {
            if ($limit > 0 && $count >= $limit) {
                break;
            }

            if (preg_match($regex, $key)) {
                $matches[] = $key;
                $count++;
            }
        }

        return $matches;
    }

    public function scanPrefix(string $prefix, int $limit = 1000): array
    {
        $matches = [];
        $count = 0;

        foreach (array_keys($this->data) as $key) {
            if ($limit > 0 && $count >= $limit) {
                break;
            }

            if (str_starts_with($key, $prefix)) {
                $matches[] = $key;
                $count++;
            }
        }

        return $matches;
    }

    public function getAllKeys(int $limit = 1000): array
    {
        $keys = array_keys($this->data);

        if ($limit > 0 && count($keys) > $limit) {
            return array_slice($keys, 0, $limit);
        }

        return $keys;
    }

    public function getScanPerformance(): array
    {
        return [
            'efficient' => true,
            'max_recommended_keys' => 10000,
            'estimated_time_per_1000_keys' => 0.001, // 1ms per 1000 keys
        ];
    }

    /**
     * Convert wildcard pattern to regex
     */
    private function patternToRegex(string $pattern): string
    {
        // Escape special regex characters except * and **
        $escaped = preg_quote($pattern, '/');

        // Replace escaped wildcards back
        $escaped = str_replace('\\*\\*', '__DEEP_WILDCARD__', $escaped);
        $escaped = str_replace('\\*', '__WILDCARD__', $escaped);

        // Convert to regex
        $regex = str_replace('__DEEP_WILDCARD__', '.*', $escaped);
        $regex = str_replace('__WILDCARD__', '[^:]*', $regex);

        return '/^' . $regex . '$/';
    }
}
