<?php
// =====================================================
// SHARK CRM - Database Driven Multi Language System
// =====================================================

if (!function_exists('crmI18nEnsureTables')) {
    function crmI18nEnsureTables($conn) {
        static $done = false;
        if ($done) return;
        $done = true;
        if (!function_exists('tableExists') || !function_exists('columnExists')) return;
        if (!tableExists($conn, 'languages')) return; // run migration first; keep runtime safe on old DB
    }

    function crmCurrentLanguageCode($conn = null) {
        static $cache = null;
        if ($cache !== null) return $cache;
        $code = $_SESSION['language_code'] ?? '';
        if (!$code && $conn && isset($_SESSION['user_id']) && function_exists('columnExists') && columnExists($conn, 'users', 'language_code')) {
            $uid = (int)$_SESSION['user_id'];
            $q = $conn->query("SELECT language_code FROM users WHERE id=$uid LIMIT 1");
            if ($q && $q->num_rows) $code = trim((string)$q->fetch_assoc()['language_code']);
        }
        if (!$code && $conn && isset($_SESSION['account_id']) && function_exists('columnExists') && columnExists($conn, 'accounts', 'default_language_code')) {
            $aid = (int)$_SESSION['account_id'];
            $q = $conn->query("SELECT default_language_code FROM accounts WHERE id=$aid LIMIT 1");
            if ($q && $q->num_rows) $code = trim((string)$q->fetch_assoc()['default_language_code']);
        }
        if (!$code && $conn && function_exists('tableExists') && tableExists($conn, 'languages')) {
            $q = $conn->query("SELECT code FROM languages WHERE is_default=1 AND status=1 LIMIT 1");
            if ($q && $q->num_rows) $code = trim((string)$q->fetch_assoc()['code']);
        }
        $cache = $code ?: 'en';
        $_SESSION['language_code'] = $cache;
        return $cache;
    }

    function crmCurrentLanguage($conn = null) {
        $code = crmCurrentLanguageCode($conn);
        $row = ['code'=>$code, 'name'=>$code, 'direction'=>'ltr'];
        if ($conn && function_exists('tableExists') && tableExists($conn, 'languages')) {
            $safe = $conn->real_escape_string($code);
            $q = $conn->query("SELECT * FROM languages WHERE code='$safe' LIMIT 1");
            if ($q && $q->num_rows) $row = $q->fetch_assoc();
        }
        return $row;
    }

    function crmTranslationCache($conn) {
        static $cache = [];
        $code = crmCurrentLanguageCode($conn);
        if (isset($cache[$code])) return $cache[$code];
        $cache[$code] = [];
        if (!$conn || !function_exists('tableExists') || !tableExists($conn, 'languages') || !tableExists($conn, 'translation_keys') || !tableExists($conn, 'translations')) return $cache[$code];
        $safe = $conn->real_escape_string($code);
        $sql = "SELECT tk.`key`, tr.value
                FROM translation_keys tk
                JOIN translations tr ON tr.key_id=tk.id
                JOIN languages l ON l.id=tr.language_id
                WHERE l.code='$safe' AND l.status=1";
        $res = $conn->query($sql);
        if ($res) while ($r = $res->fetch_assoc()) $cache[$code][$r['key']] = $r['value'];
        return $cache[$code];
    }

    function crmRegisterTranslationKey($conn, $key, $fallback = '', $group = 'system') {
        if (!$conn || !function_exists('tableExists') || !tableExists($conn, 'translation_keys')) return;
        $key = trim((string)$key);
        if ($key === '') return;
        $ek = $conn->real_escape_string($key);
        $eg = $conn->real_escape_string($group ?: 'system');
        $ed = $conn->real_escape_string(mb_substr((string)$fallback, 0, 250));
        $conn->query("INSERT IGNORE INTO translation_keys (`key`, group_name, description) VALUES ('$ek', '$eg', '$ed')");
        if (function_exists('tableExists') && tableExists($conn, 'languages') && tableExists($conn, 'translations')) {
            $conn->query("INSERT IGNORE INTO translations (language_id, key_id, value)
                          SELECT l.id, tk.id, '$ed'
                          FROM languages l JOIN translation_keys tk ON tk.`key`='$ek'
                          WHERE l.is_default=1");
        }
    }

    function __($key, $fallback = null, $vars = []) {
        global $conn;
        $fallback = $fallback === null ? $key : $fallback;
        if ($conn ?? null) crmRegisterTranslationKey($conn, $key, $fallback, explode('.', $key)[0] ?: 'system');
        $dict = ($conn ?? null) ? crmTranslationCache($conn) : [];
        $text = $dict[$key] ?? $fallback;
        foreach ((array)$vars as $k=>$v) $text = str_replace('{' . $k . '}', (string)$v, $text);
        return $text;
    }

    function _e($key, $fallback = null, $vars = []) {
        echo htmlspecialchars(__($key, $fallback, $vars), ENT_QUOTES, 'UTF-8');
    }

    function crmMessageKey($message) {
        $message = trim((string)$message);
        $base = preg_replace('/\s+/', ' ', mb_strtolower($message));
        $base = preg_replace('/[^\pL\pN]+/u', '_', $base);
        $base = trim($base, '_');
        if ($base === '' || mb_strlen($base) > 70) $base = substr(sha1($message), 0, 12);
        return 'messages.' . $base;
    }

    function crmTranslateMessage($message, $vars = []) {
        if ($message === '' || $message === null) return $message;
        return __(crmMessageKey($message), $message, $vars);
    }

    function crmAutoKey($text) { return 'auto.' . substr(sha1(trim((string)$text)), 0, 16); }

    function crmShouldAutoTranslate($text) {
        $t = trim(html_entity_decode((string)$text, ENT_QUOTES, 'UTF-8'));
        if ($t === '' || mb_strlen($t) < 2 || mb_strlen($t) > 90) return false;
        if (preg_match('/^(https?:|www\.|[\w.%-]+@[\w.-]+|[#@$]\s*\d|\d+[\d\s.,:%\/-]*)$/u', $t)) return false;
        if (!preg_match('/[\pL]/u', $t)) return false;
        return true;
    }

    function crmTranslatePlainText($text) {
        global $conn;
        $original = html_entity_decode((string)$text, ENT_QUOTES, 'UTF-8');
        if (!crmShouldAutoTranslate($original)) return $text;
        $key = crmAutoKey($original);
        if ($conn ?? null) crmRegisterTranslationKey($conn, $key, $original, 'auto');
        return __($key, $original);
    }

    function crmTranslateHtml($html) {
        if (!is_string($html) || $html === '') return $html;
        $parts = preg_split('/(<script\b[^>]*>.*?<\/script>|<style\b[^>]*>.*?<\/style>|<textarea\b[^>]*>.*?<\/textarea>)/is', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
        foreach ($parts as $i => $part) {
            if (preg_match('/^<(script|style|textarea)\b/is', $part)) continue;
            $part = preg_replace_callback('/>([^<>]+)</u', function($m){
                // Preserve only whitespace around the text.
                // Old code removed the first character and kept the rest of the original text,
                // which caused labels like DashboardDashboardDashboard.
                $raw = (string)$m[1];
                $trim = trim($raw);
                if (!crmShouldAutoTranslate($trim)) return $m[0];
                preg_match('/^\s*/u', $raw, $leadMatch);
                preg_match('/\s*$/u', $raw, $trailMatch);
                $left = $leadMatch[0] ?? '';
                $right = $trailMatch[0] ?? '';
                return '>' . $left . htmlspecialchars(crmTranslatePlainText($trim), ENT_QUOTES, 'UTF-8') . $right . '<';
            }, $part);
            $part = preg_replace_callback('/\b(placeholder|title|aria-label)="([^"]+)"/u', function($m){
                if (!crmShouldAutoTranslate($m[2])) return $m[0];
                return $m[1] . '="' . htmlspecialchars(crmTranslatePlainText($m[2]), ENT_QUOTES, 'UTF-8') . '"';
            }, $part);
            $parts[$i] = $part;
        }
        return implode('', $parts);
    }

    function crmStartAutoTranslate() { if (!ob_get_level()) ob_start(); else ob_start(); }
    function crmEndAutoTranslate() {
        $html = ob_get_clean();
        echo crmTranslateHtml($html);
    }

    function crmJsTranslations($conn) {
        $dict = crmTranslationCache($conn);
        $out = [];
        foreach ($dict as $k=>$v) {
            if (strpos($k, 'js.') === 0 || strpos($k, 'messages.') === 0 || strpos($k, 'buttons.') === 0) $out[$k] = $v;
        }
        return $out;
    }
}
?>
