<?php

declare(strict_types=1);

namespace App\Application\Debug;

use App\Framework\OutputBuffer\OutputBuffer;

/**
 * Debug Code Evaluator
 *
 * Safely evaluates PHP code in a controlled environment.
 * Captures output, return values, and execution time.
 */
final readonly class DebugCodeEvaluator
{
    public function evaluate(string $code): EvaluationResult
    {
        $startTime = microtime(true);

        // Capture output using framework's OutputBuffer
        $captured = OutputBuffer::capture(function () use ($code, &$returnValue) {
            // Evaluate code
            // Note: eval is used here intentionally for REPL functionality
            // This is ONLY safe because it's restricted to development mode
            $returnValue = eval($code);
            return $returnValue;
        });

        $type = get_debug_type($captured->result);
        $output = $captured->content;

        // Format output
        if ($captured->result !== null) {
            $formattedValue = $this->formatValue($captured->result);
            $output .= $formattedValue;
        }

        $executionTime = microtime(true) - $startTime;

        return new EvaluationResult(
            output: $output ?: '(no output)',
            type: $type,
            executionTime: $executionTime,
            rawValue: $captured->result
        );
    }

    private function formatValue(mixed $value): string
    {
        if (is_scalar($value)) {
            return var_export($value, true);
        }

        if (is_array($value)) {
            return $this->formatArray($value);
        }

        if (is_object($value)) {
            return $this->formatObject($value);
        }

        return var_export($value, true);
    }

    private function formatArray(array $array): string
    {
        if (empty($array)) {
            return '[]';
        }

        $formatted = "[\n";
        foreach ($array as $key => $value) {
            $formattedKey = is_string($key) ? "'{$key}'" : $key;
            $formattedValue = is_scalar($value) ? var_export($value, true) : get_debug_type($value);
            $formatted .= "  {$formattedKey} => {$formattedValue},\n";
        }
        $formatted .= ']';

        return $formatted;
    }

    private function formatObject(object $object): string
    {
        $reflection = new \ReflectionClass($object);
        $className = $reflection->getName();

        $properties = [];
        foreach ($reflection->getProperties() as $property) {
            $property->setAccessible(true);
            $name = $property->getName();
            $value = $property->getValue($object);
            $properties[$name] = is_scalar($value) ? $value : get_debug_type($value);
        }

        return sprintf(
            "%s {\n%s\n}",
            $className,
            $this->formatArray($properties)
        );
    }
}
