<?php
/**
 * class/app.php — clase base de kamiquery.
 *
 * Helpers globales transversales que heredan el resto de clases de módulo:
 * escape de salida, redirecciones, log, mensajes/errores e i18n.
 */
if (!defined('IN_APP')) { die('Acceso denegado'); }

class App
{
    /** @var mysqli */
    protected $db;
    /** @var array */
    protected $config;

    /** Mensaje/erro de la petición (se renderizan con check_*). */
    public $_message = '';
    public $_error   = '';

    // --- Estado i18n / tema (compartido por todas las instancias) ---
    protected static $locale  = 'es';
    protected static $strings = array();
    protected static $cfg     = array();

    public function __construct($db = null, $config = array())
    {
        $this->db     = $db;
        $this->config = $config;
        if ($config) self::$cfg = $config;
    }

    /* ----------------------------------------------------------------- *
     *  Salida / utilidades
     * ----------------------------------------------------------------- */

    /** Escapa para HTML. Úsalo SIEMPRE al pintar datos. */
    public static function e($value)
    {
        return htmlspecialchars((string)$value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }

    /** Redirección y corte de ejecución. */
    public static function redirect($url)
    {
        header('Location: ' . $url);
        exit;
    }

    /** Construye una URL amigable de la app, siempre con barra final: App::url('chat/12') -> '/chat/12/'. */
    public static function url($path = '')
    {
        $path = trim((string)$path, '/');
        return $path === '' ? '/' : '/' . $path . '/';
    }

    /** Log a error_log con prefijo de la app. */
    public static function log($msg)
    {
        error_log('[kamiquery] ' . (is_scalar($msg) ? $msg : json_encode($msg)));
    }

    /** Token CSRF de la sesión de navegación (HMAC con el secreto). */
    public static function csrf()
    {
        if (empty($_COOKIE['auth'])) return '';
        return hash_hmac('sha256', $_COOKIE['auth'], self::$cfg['secret'] ?? '');
    }

    public static function checkCsrf($token)
    {
        $good = self::csrf();
        return $good !== '' && is_string($token) && hash_equals($good, $token);
    }

    /* ----------------------------------------------------------------- *
     *  Mensajes y errores
     * ----------------------------------------------------------------- */

    public function check_messageAndError()
    {
        $this->check_message();
        $this->check_error();
    }

    public function check_message()
    {
        if ($this->_message === '') return;
        echo '<div class="mb-4 rounded-lg border border-success/30 bg-success/10 px-4 py-3 text-sm text-success">'
            . self::e($this->_message) . '</div>';
    }

    public function check_error()
    {
        if ($this->_error === '') return;
        echo '<div class="mb-4 rounded-lg border border-danger/30 bg-danger/10 px-4 py-3 text-sm text-danger">'
            . self::e($this->_error) . '</div>';
    }

    /* ----------------------------------------------------------------- *
     *  i18n
     * ----------------------------------------------------------------- */

    /**
     * Resuelve el idioma activo y carga lang/{locale}.php.
     * Prioridad: cookie lang -> users.locale -> Accept-Language -> default.
     */
    public function loadLocale($user = null)
    {
        $allowed = self::$cfg['locales'] ?? array('es');
        $default = self::$cfg['default_locale'] ?? 'es';
        $locale  = null;

        if (!empty($_COOKIE['lang']) && in_array($_COOKIE['lang'], $allowed, true)) {
            $locale = $_COOKIE['lang'];
        } elseif ($user && !empty($user['locale']) && in_array($user['locale'], $allowed, true)) {
            $locale = $user['locale'];
        } elseif (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            $pref = strtolower(substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2));
            if (in_array($pref, $allowed, true)) $locale = $pref;
        }
        if ($locale === null) $locale = $default;

        self::$locale = $locale;
        $file = __DIR__ . '/../lang/' . $locale . '.php';
        self::$strings = is_file($file) ? (include $file) : array();
    }

    public static function locale()
    {
        return self::$locale;
    }

    /**
     * Traduce una clave i18n. Interpola {marcadores} con $vars.
     * Fallback: si falta la clave, devuelve la propia clave (para detectar huecos).
     */
    public static function t($key, $vars = array())
    {
        $str = self::$strings[$key] ?? $key;
        if ($vars) {
            foreach ($vars as $k => $v) {
                $str = str_replace('{' . $k . '}', (string)$v, $str);
            }
        }
        return $str;
    }

    /* ----------------------------------------------------------------- *
     *  Settings (configuración global clave-valor)
     * ----------------------------------------------------------------- */

    /** Lee un ajuste global; devuelve $default si no existe. */
    public function getSetting($key, $default = null)
    {
        $this->ensureSchema('settings');
        $stmt = $this->db->prepare('SELECT svalue FROM settings WHERE skey = ? LIMIT 1');
        $stmt->bind_param('s', $key);
        $stmt->execute();
        $row = $stmt->get_result()->fetch_assoc();
        $stmt->close();
        return $row ? $row['svalue'] : $default;
    }

    /** Crea o actualiza un ajuste global. */
    public function setSetting($key, $value)
    {
        $this->ensureSchema('settings');
        $stmt = $this->db->prepare('INSERT INTO settings (skey, svalue) VALUES (?, ?) ON DUPLICATE KEY UPDATE svalue = VALUES(svalue)');
        $stmt->bind_param('ss', $key, $value);
        $stmt->execute();
        $stmt->close();
    }

    /* ----------------------------------------------------------------- *
     *  Esquema (creación perezosa)
     * ----------------------------------------------------------------- */

    /** Ejecuta el .sql de inc/schemas/{name}.sql (CREATE TABLE IF NOT EXISTS). */
    protected function ensureSchema($name)
    {
        $file = __DIR__ . '/../inc/schemas/' . $name . '.sql';
        if (!is_file($file)) return;
        $sql = file_get_contents($file);
        // Permite varias sentencias separadas por ';'
        foreach (array_filter(array_map('trim', explode(';', $sql))) as $stmt) {
            $this->db->query($stmt);
        }
    }

    /** Añade una columna a una tabla si todavía no existe (migración idempotente). */
    protected function ensureColumn($table, $column, $definition)
    {
        $table  = preg_replace('/[^a-zA-Z0-9_]/', '', $table);
        $column = preg_replace('/[^a-zA-Z0-9_]/', '', $column);
        $res = $this->db->query("SHOW COLUMNS FROM `$table` LIKE '$column'");
        if ($res && $res->num_rows === 0) {
            $this->db->query("ALTER TABLE `$table` ADD COLUMN `$column` $definition");
        }
    }

    /** Garantiza las columnas de organización en la tabla users. */
    protected function ensureOrgColumns()
    {
        $this->ensureSchema('users');
        $this->ensureColumn('users', 'is_org', 'TINYINT UNSIGNED NOT NULL DEFAULT 0');
        $this->ensureColumn('users', 'org_id', 'INT UNSIGNED NULL');
        $this->ensureColumn('users', 'max_accounts', 'INT UNSIGNED NOT NULL DEFAULT 0');
    }
}
