<?php

namespace App\Core;

/**
 * ITSWEBER CMS – View / Template Engine
 *
 * Renders PHP templates with theme fallback support.
 */
class View
{
    private string $themesPath;
    private string $activeTheme;
    private string $basePath;
    private array $sharedData = [];

    /** @var array View data from the most recent render() call (available for section templates) */
    private array $currentRenderData = [];

    public function __construct(string $themesPath, string $activeTheme, string $basePath)
    {
        $this->themesPath = rtrim($themesPath, '/');
        $this->activeTheme = $activeTheme;
        $this->basePath = $basePath;
    }

    /**
     * Render a theme template.
     */
    public function render(string $template, array $data = []): string
    {
        $file = $this->resolveTemplate($template);
        if (!$file) {
            throw new \RuntimeException("Template '{$template}' not found.");
        }
        return $this->renderFile($file, array_merge($this->sharedData, $data));
    }

    /**
     * Render an admin view.
     */
    public function renderAdmin(string $view, array $data = []): string
    {
        $file = $this->basePath . '/admin/views/' . $view . '.php';
        if (!file_exists($file)) {
            throw new \RuntimeException("Admin view '{$view}' not found.");
        }
        return $this->renderFile($file, array_merge($this->sharedData, $data));
    }

    /**
     * Render an installer view.
     */
    public function renderInstaller(string $view, array $data = []): string
    {
        $file = $this->basePath . '/installer/views/' . $view . '.php';
        if (!file_exists($file)) {
            throw new \RuntimeException("Installer view '{$view}' not found.");
        }
        return $this->renderFile($file, array_merge($this->sharedData, $data));
    }

    /**
     * Share data with all views.
     */
    public function share(string $key, mixed $value): void
    {
        $this->sharedData[$key] = $value;
    }

    /**
     * Resolve template path with fallback: active theme → default theme.
     */
    private function resolveTemplate(string $template): ?string
    {
        // Defense-in-depth: prevent path traversal
        if (str_contains($template, '..') || str_contains($template, "\0")) {
            return null;
        }

        // Try active theme
        $path = $this->themesPath . '/' . $this->activeTheme . '/templates/' . $template . '.php';
        if (file_exists($path)) {
            return $path;
        }

        // Fallback to default theme
        if ($this->activeTheme !== 'default') {
            $path = $this->themesPath . '/default/templates/' . $template . '.php';
            if (file_exists($path)) {
                return $path;
            }
        }

        return null;
    }

    /**
     * Render a PHP file with extracted data.
     */
    private function renderFile(string $file, array $data): string
    {
        $this->currentRenderData = $data;
        extract($data, EXTR_SKIP);
        ob_start();
        include $file;
        return ob_get_clean();
    }

    /**
     * Get the view data from the current render pass.
     * Used by render_section_file() to forward controller variables
     * (e.g. $latest_posts, $upcoming_events) to section templates.
     */
    public function currentRenderData(): array
    {
        return $this->currentRenderData;
    }

    /**
     * Seed render data for preview context (AJAX).
     * Section templates may reference variables like $latest_posts that are
     * normally set by the homepage controller. This avoids undefined-variable
     * errors when rendering a section outside a full page render.
     */
    public function setPreviewData(array $data): void
    {
        $this->currentRenderData = array_merge($this->currentRenderData, $data);
    }

    /**
     * Get the active theme name.
     */
    public function activeTheme(): string
    {
        return $this->activeTheme;
    }

    /**
     * Set the active theme.
     */
    public function setActiveTheme(string $theme): void
    {
        $this->activeTheme = $theme;
    }

    /**
     * Get the URL path to a theme asset with cache-busting version string.
     */
    public function themeAsset(string $path): string
    {
        $url = '/themes/' . $this->activeTheme . '/assets/' . ltrim($path, '/');

        // Cachebuster: filemtime preferred (invalidates on every file edit),
        // falls back to CMS version if the local file can't be stat'd.
        $basePath = app()->basePath();
        $localPath = $basePath . '/public' . $url;
        $buster = @filemtime($localPath) ?: (function_exists('cms_version') ? cms_version() : '1');

        return $url . '?v=' . urlencode((string) $buster);
    }
}
