<?php

namespace App\Core;

/**
 * ITSWEBER CMS – Application Bootstrap
 *
 * Central application class. Initializes core services,
 * handles the request lifecycle, and acts as a simple service container.
 */
class App
{
    private static ?App $instance = null;
    private array $services = [];
    private string $basePath;

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

    /**
     * Initialize the application (singleton).
     */
    public static function init(string $basePath): self
    {
        if (self::$instance === null) {
            self::$instance = new self($basePath);
            self::$instance->bootstrap();
        }
        return self::$instance;
    }

    /**
     * Get the application instance.
     */
    public static function getInstance(): ?self
    {
        return self::$instance;
    }

    /**
     * Bootstrap core services.
     */
    private function bootstrap(): void
    {
        // Error reporting
        $this->setupErrorHandling();

        // Load configuration
        $this->registerService('config', new Config($this->basePath . '/app/Config'));

        // Session
        $session = new Session();
        $session->start();
        $this->registerService('session', $session);

        // Database (only if installed)
        if ($this->isInstalled()) {
            $dbConfig = $this->config()->get('database');
            if ($dbConfig) {
                $db = new Database($dbConfig);
                $this->registerService('database', $db);
            }
        }

        // CSRF
        $this->registerService('csrf', new Csrf($session));

        // Translator – determine system language
        $locale = 'de'; // Default fallback for all scenarios
        if ($this->isInstalled() && $this->hasService('database')) {
            $dbLocale = $this->getSetting('system_language', '');
            if ($dbLocale !== '' && Translator::isSupported($dbLocale)) {
                $locale = $dbLocale;
            }
        }
        // Session override (installer flow or admin language switch)
        $sessionLocale = $session->get('locale', '');
        if ($sessionLocale !== '' && Translator::isSupported($sessionLocale)) {
            $locale = $sessionLocale;
        }
        $this->registerService('translator', new Translator(
            $this->basePath . '/app/Lang',
            $locale,
            'de'
        ));

        // Auth
        if ($this->isInstalled() && $this->hasService('database')) {
            $this->registerService('auth', new Auth($this->service('database'), $session));
        }

        // View
        $themePath = $this->basePath . '/themes';
        $activeTheme = 'default';
        if ($this->isInstalled() && $this->hasService('database')) {
            $activeTheme = $this->getSetting('active_theme', 'default');
        }

        // Theme-Fallback: if the configured theme directory does not exist,
        // silently fall back to 'default' and persist a session flag so the
        // admin UI can show a persistent warning banner (ADR-088).
        if ($activeTheme !== 'default' && !is_dir($themePath . '/' . $activeTheme)) {
            $session->set('_iw_theme_fallback', $activeTheme);
            $activeTheme = 'default';
        } else {
            // Clear a stale fallback flag once the theme is available again.
            $session->remove('_iw_theme_fallback');
        }

        $this->registerService('view', new View($themePath, $activeTheme, $this->basePath));

        // Router
        $this->registerService('router', new Router($this));
    }

    /**
     * Run the application – dispatch the current request.
     */
    public function run(): void
    {
        $router = $this->service('router');
        $request = Request::capture();
        $this->registerService('request', $request);

        // Check if installer should be shown
        if (!$this->isInstalled()) {
            $uri = $request->uri();
            if (strpos($uri, '/installer') !== 0) {
                Response::redirect('/installer');
                return;
            }
        }

        $router->dispatch($request);
    }

    /**
     * Register a service in the container.
     */
    public function registerService(string $name, object $service): void
    {
        $this->services[$name] = $service;
    }

    /**
     * Retrieve a service from the container.
     */
    public function service(string $name): object
    {
        if (!isset($this->services[$name])) {
            throw new \RuntimeException("Service '{$name}' not registered.");
        }
        return $this->services[$name];
    }

    /**
     * Check if a service exists.
     */
    public function hasService(string $name): bool
    {
        return isset($this->services[$name]);
    }

    /**
     * Shortcut: Config service.
     */
    public function config(): Config
    {
        return $this->services['config'];
    }

    /**
     * Shortcut: Database service.
     */
    public function db(): Database
    {
        return $this->services['database'];
    }

    /**
     * Get base path.
     */
    public function basePath(string $append = ''): string
    {
        return $this->basePath . ($append ? '/' . ltrim($append, '/') : '');
    }

    /**
     * Check if CMS is installed.
     */
    public function isInstalled(): bool
    {
        return file_exists($this->basePath . '/storage/.installed');
    }

    /**
     * Get a setting from the database.
     */
    public function getSetting(string $key, string $default = ''): string
    {
        if (!$this->hasService('database')) {
            return $default;
        }
        try {
            $db = $this->db();
            $stmt = $db->query(
                "SELECT setting_value FROM iw_settings WHERE setting_key = :key LIMIT 1",
                ['key' => $key]
            );
            $row = $stmt->fetch(\PDO::FETCH_ASSOC);
            return $row ? $row['setting_value'] : $default;
        } catch (\Exception $e) {
            return $default;
        }
    }

    /**
     * Setup error handling based on environment.
     */
    private function setupErrorHandling(): void
    {
        $isDebug = file_exists($this->basePath . '/.debug');
        if ($isDebug) {
            error_reporting(E_ALL);
            ini_set('display_errors', '1');
        } else {
            error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
            ini_set('display_errors', '0');
        }
        ini_set('log_errors', '1');
        ini_set('error_log', $this->basePath . '/storage/logs/error.log');
    }
}
