<?php
// CoreFlux/core/View.php

namespace Core;

use Exception;
use Throwable; // Import the Throwable interface for broader exception handling

class View
{
    private $viewName;
    private $data = [];
    private $layout = null;
    private $minify = true; // Minification is on by default

    public function __construct(string $viewName)
    {
        $this->viewName = $viewName;
    }

    public static function make(string $viewName): self
    {
        return new self($viewName);
    }

    public function setLayout(string $layoutName): self
    {
        $this->layout = $layoutName;
        return $this;
    }

    public function with(array $data): self
    {
        $this->data = array_merge($this->data, $data);
        return $this;
    }

    /**
     * Public method to disable minification for the current view.
     * @return $this
     */
    public function dontMinify(): self
    {
        $this->minify = false;
        return $this;
    }

    /**
     * Renders the view (or layout), processes variables, and minifies the output if enabled.
     */
    public function render()
    {
        $viewFile = FS_APP_PATH . DIRECTORY_SEPARATOR . 'Views' . DIRECTORY_SEPARATOR . $this->viewName . '.view.php';
        
        if (!file_exists($viewFile)) {
            throw new Exception("View file not found at: {$viewFile}");
        }

        ob_start();

        if ($this->layout) {
            $layoutFile = FS_APP_PATH . DIRECTORY_SEPARATOR . 'layouts' . DIRECTORY_SEPARATOR . $this->layout . '.php';
            if (!file_exists($layoutFile)) {
                throw new Exception("Layout file not found at: {$layoutFile}");
            }
            // When including the layout, the layout itself must include the view file.
            // We pass the view file path as a variable to the layout.
            $viewContentPath = $viewFile;
            require_once $layoutFile;
        } else {
            // If no layout, just include the view file directly.
            require_once $viewFile;
        }

        $content = ob_get_clean();
        
        // CRITICAL FIX: The order of operations is important.
        // First, replace PHP-style directives like @if and @foreach
        $processedContent = $this->replaceDirectives($content);

        // Then, replace variable placeholders like {{$variable}}
        $processedContent = $this->replaceVariables($processedContent);

        // Finally, minify the content if minification is enabled.
        if ($this->minify) {
            $processedContent = $this->minifyHtml($processedContent);
        }

        echo $processedContent;
    }

    /**
     * Replaces placeholders with data values.
     * @param string $content The raw HTML content from the view.
     * @return string The content with variables replaced.
     */
    private function replaceVariables(string $content): string
    {
        // Regex pattern to match {{$variableName}}
        $pattern = '/\{\{\$([a-zA-Z0-9_]+)\}\}/';
        return preg_replace_callback($pattern, function($matches) {
            $variableName = $matches[1];
            if (array_key_exists($variableName, $this->data)) {
                // CRITICAL FIX: Check if the data is an array before trying to echo it.
                // If it is, return a helpful error message.
                $value = $this->data[$variableName];
                if (is_array($value)) {
                    // This error will help the developer debug the view.
                    return "[VIEW ERROR: Cannot display array in placeholder {{$variableName}}]";
                }
                return htmlspecialchars($value);
            }
            return ''; // Return an empty string if the variable is not found
        }, $content);
    }

    /**
     * Replaces custom directives (@if, @foreach) with PHP code blocks.
     * @param string $content
     * @return string
     */
    private function replaceDirectives(string $content): string
    {
        // Start output buffering to capture the evaluated PHP code.
        ob_start();

        // Pass the data array to the temporary file so it's in scope.
        $data = $this->data;
        $tempFile = tempnam(sys_get_temp_dir(), 'coreflux_view_');

        // Replace directives and save to a temporary file.
        $content = preg_replace('/@if\s*\((.*)\)/', '<?php if($1): ?>', $content);
        $content = preg_replace('/@else/', '<?php else: ?>', subject: $content);
        $content = preg_replace('/@endif/', '<?php endif; ?>', $content);
        $content = preg_replace('/@foreach\s*\((.*)\)/', '<?php foreach($1): ?>', $content);
        $content = preg_replace('/@endforeach/', '<?php endforeach; ?>', $content);
        
        // CRITICAL: Replace the shorthand tag with standard PHP
        $content = preg_replace('/<\?=(.*?)\?>/', '<?php echo htmlspecialchars($1); ?>', $content);

        // The temporary file will contain the standard PHP code blocks
        // We include it to execute the conditional and loop logic.
        file_put_contents($tempFile, $content);

        // The closure is used to execute the temporary file within the current scope,
        // making the $data variables available to it.
        $renderClosure = function($tempFile, $data) {
            extract($data);
            include $tempFile;
        };
        $renderClosure($tempFile, $this->data);

        $evaluatedContent = ob_get_clean();
        unlink($tempFile); // Clean up the temporary file

        return $evaluatedContent;
    }
    
    /**
     * Minifies the HTML by removing whitespace and comments.
     * @param string $html The HTML content to minify.
     * @return string The minified HTML.
     */
    private function minifyHtml(string $html): string
    {
        // This is the same minify logic as before.
        $html = preg_replace('/<!--(.|\s)*?-->/', '', $html);
        $html = preg_replace('/^[\s]+|^[\t]+/m', '', $html);
        $html = preg_replace('/[\r\n]+/', '', $html);
        return $html;
    }
}