<?php
declare(strict_types=1);

/**
 * /includes/start.php
 * Відповідає за:
 * - bootstrap для всіх сторінок
 * - підключення config/helpers/db/auth/i18n
 * - старт сесії
 * - timezone
 * - request context globals
 * - нормалізацію мови (тільки uk|ru|en)
 * - clean redirect з ?lang=... на чистий URL
 * - ініціалізацію $pdo, $me, $siteName
 * - авто-логування трафіку
 * - авто-оновлення users.last_seen_at для авторизованого користувача
 *
 * SEO-ОНОВЛЕНО:
 * - сесія стартує ДО i18n
 * - мовна логіка не дублюється хаотично
 * - якщо відкрили URL з ?lang=..., мова фіксується в session/cookie,
 *   а потім іде 301 на чистий URL без lang
 *
 * НОВЕ:
 * - при кожному звичайному HTTP-запиті для залогіненого юзера
 *   оновлюється users.last_seen_at
 * - оновлення дроселюється по сесії (не частіше 1 разу на 60 сек),
 *   щоб не робити зайві UPDATE на кожному кліку/AJAX
 */

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/db.php';
require_once __DIR__ . '/auth.php';
require_once __DIR__ . '/account_restrictions.php';

start_app_session();

/* ===== installer redirect guard ===== */
if (!function_exists('start_is_install_request')) {
    function start_is_install_request(string $reqPath): bool
    {
        if ($reqPath === '/install.php' || $reqPath === '/install') {
            return true;
        }

        return strncmp($reqPath, '/install/', 9) === 0;
    }
}

if (!function_exists('start_is_installed')) {
    function start_is_installed(): bool
    {
        return is_file(dirname(__DIR__) . '/.installed');
    }
}

$__installReqUri = (string)($_SERVER['REQUEST_URI'] ?? '/');
$__installReqPath = (string)parse_url($__installReqUri, PHP_URL_PATH);
if ($__installReqPath === '') $__installReqPath = '/';

if (!start_is_installed() && !start_is_install_request($__installReqPath) && PHP_SAPI !== 'cli' && !headers_sent()) {
    header('Location: /install.php');
    exit;
}

unset($__installReqUri, $__installReqPath);

require_once __DIR__ . '/i18n.php';

$cfg = app_config();
date_default_timezone_set((string)($cfg['app']['timezone'] ?? 'Europe/Kyiv'));

/* ===== request context ===== */
$reqUri  = (string)($_SERVER['REQUEST_URI'] ?? '/');
$reqPath = (string)parse_url($reqUri, PHP_URL_PATH);
if ($reqPath === '') $reqPath = '/';

$isAjax = false;
$xhr = (string)($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '');
if ($xhr !== '' && strtolower($xhr) === 'xmlhttprequest') {
    $isAjax = true;
}

$isAdmin      = (strncmp($reqPath, '/admin/', 7) === 0 || $reqPath === '/admin');
$isFunctional = (strncmp($reqPath, '/functional/', 12) === 0 || $reqPath === '/functional');

$GLOBALS['_REQ_URI']       = $reqUri;
$GLOBALS['_REQ_PATH']      = $reqPath;
$GLOBALS['_IS_AJAX']       = $isAjax;
$GLOBALS['_IS_ADMIN']      = $isAdmin;
$GLOBALS['_IS_FUNCTIONAL'] = $isFunctional;

/* ===== language helpers (compat with old/new i18n.php) ===== */
if (!function_exists('start_allowed_langs')) {
    function start_allowed_langs(array $cfg): array
    {
        if (function_exists('i18n_allowed_langs')) {
            try {
                $langs = i18n_allowed_langs();
                if (is_array($langs) && $langs) {
                    return array_values($langs);
                }
            } catch (Throwable $e) {}
        }

        $allowed = $cfg['app']['langs'] ?? ['uk', 'ru', 'en'];
        if (!is_array($allowed) || !$allowed) {
            $allowed = ['uk', 'ru', 'en'];
        }

        $allowed = array_values(array_unique(array_map(static function ($v) {
            return strtolower(trim((string)$v));
        }, $allowed)));

        $allowed = array_values(array_intersect($allowed, ['uk', 'ru', 'en']));
        return $allowed ?: ['uk', 'ru', 'en'];
    }
}

if (!function_exists('start_default_lang')) {
    function start_default_lang(array $cfg, array $allowed): string
    {
        if (function_exists('i18n_default_lang')) {
            try {
                $def = (string)i18n_default_lang($allowed);
                if (in_array($def, $allowed, true)) {
                    return $def;
                }
            } catch (Throwable $e) {}
        }

        $def = strtolower(trim((string)($cfg['app']['default_lang'] ?? 'uk')));
        return in_array($def, $allowed, true) ? $def : 'uk';
    }
}

if (!function_exists('start_normalize_lang')) {
    function start_normalize_lang(?string $lang, array $allowed, string $fallback): string
    {
        if (function_exists('i18n_normalize_lang')) {
            try {
                return (string)i18n_normalize_lang((string)$lang, $allowed, $fallback);
            } catch (Throwable $e) {}
        }

        $lang = strtolower(trim((string)$lang));
        return in_array($lang, $allowed, true) ? $lang : $fallback;
    }
}

if (!function_exists('start_is_https_request')) {
    function start_is_https_request(): bool
    {
        if (function_exists('i18n_is_https_request')) {
            try {
                return (bool)i18n_is_https_request();
            } catch (Throwable $e) {}
        }

        $cfv = (string)($_SERVER['HTTP_CF_VISITOR'] ?? '');
        if ($cfv !== '') {
            $j = json_decode($cfv, true);
            if (is_array($j) && isset($j['scheme']) && is_string($j['scheme'])) {
                return strtolower(trim($j['scheme'])) === 'https';
            }
        }

        $xfp = (string)($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '');
        if ($xfp !== '') {
            $first = strtolower(trim(explode(',', $xfp)[0]));
            if ($first === 'https') return true;
        }

        $xfs = strtolower(trim((string)($_SERVER['HTTP_X_FORWARDED_SSL'] ?? '')));
        if ($xfs === 'on' || $xfs === '1' || $xfs === 'true') return true;

        $https = strtolower(trim((string)($_SERVER['HTTPS'] ?? '')));
        return ($https !== '' && $https !== 'off' && $https !== '0');
    }
}

if (!function_exists('start_set_lang_cookie')) {
    function start_set_lang_cookie(string $lang): void
    {
        if (function_exists('i18n_set_cookie')) {
            try {
                i18n_set_cookie($lang);
                return;
            } catch (Throwable $e) {}
        }

        $currentCookieLang = (string)($_COOKIE['lang'] ?? '');
        if ($currentCookieLang === $lang) return;

        $cookieOpts = [
            'expires'  => time() + 3600 * 24 * 365,
            'path'     => '/',
            'secure'   => start_is_https_request(),
            'httponly' => true,
            'samesite' => 'Lax',
        ];
        setcookie('lang', $lang, $cookieOpts);
    }
}

if (!function_exists('start_clean_lang_url')) {
    function start_clean_lang_url(string $url): string
    {
        $url = trim($url);
        if ($url === '') return '/';

        $parts = parse_url($url);
        if (!is_array($parts)) return '/';

        $path = (string)($parts['path'] ?? '/');
        if ($path === '') $path = '/';

        $qs = [];
        if (!empty($parts['query'])) {
            parse_str((string)$parts['query'], $qs);
            if (!is_array($qs)) $qs = [];
        }

        foreach (array_keys($qs) as $k) {
            $lk = strtolower((string)$k);

            if ($lk === 'lang') {
                unset($qs[$k]);
                continue;
            }

            if (str_starts_with($lk, 'utm_')) {
                unset($qs[$k]);
                continue;
            }

            if (in_array($lk, [
                'gclid','fbclid','yclid','ymclid','gbraid','wbraid',
                '_ga','_gid','_gac','_gl','mc_cid','mc_eid',
                'ref','refid','source','src','spm','from','igshid'
            ], true)) {
                unset($qs[$k]);
                continue;
            }
        }

        if (!empty($qs)) {
            ksort($qs);
        }

        $out = $path;
        if (!empty($qs)) {
            $out .= '?' . http_build_query($qs);
        }

        if (!empty($parts['fragment'])) {
            $out .= '#' . (string)$parts['fragment'];
        }

        return $out;
    }
}

/* ===== DB schema helpers ===== */
if (!function_exists('start_db_table_exists')) {
    function start_db_table_exists(PDO $pdo, string $table): bool
    {
        static $cache = [];

        $k = 't:' . $table;
        if (array_key_exists($k, $cache)) {
            return (bool)$cache[$k];
        }

        try {
            $st = $pdo->prepare("
                SELECT 1
                FROM information_schema.tables
                WHERE table_schema = DATABASE()
                  AND table_name = ?
                LIMIT 1
            ");
            $st->execute([$table]);
            $cache[$k] = (bool)$st->fetchColumn();
        } catch (Throwable $e) {
            $cache[$k] = false;
        }

        return (bool)$cache[$k];
    }
}

if (!function_exists('start_db_column_exists')) {
    function start_db_column_exists(PDO $pdo, string $table, string $column): bool
    {
        static $cache = [];

        $k = 'c:' . $table . ':' . $column;
        if (array_key_exists($k, $cache)) {
            return (bool)$cache[$k];
        }

        try {
            $st = $pdo->prepare("
                SELECT 1
                FROM information_schema.columns
                WHERE table_schema = DATABASE()
                  AND table_name = ?
                  AND column_name = ?
                LIMIT 1
            ");
            $st->execute([$table, $column]);
            $cache[$k] = (bool)$st->fetchColumn();
        } catch (Throwable $e) {
            $cache[$k] = false;
        }

        return (bool)$cache[$k];
    }
}

if (!function_exists('start_touch_user_last_seen')) {
    function start_touch_user_last_seen(PDO $pdo, ?array $me = null): void
    {
        if (!is_array($me)) return;

        $userId = (int)($me['id'] ?? 0);
        if ($userId <= 0) return;

        if (!start_db_table_exists($pdo, 'users')) return;
        if (!start_db_column_exists($pdo, 'users', 'last_seen_at')) return;

        start_app_session();

        $sessionKey = '_last_seen_touch';
        $touchMap = $_SESSION[$sessionKey] ?? [];
        if (!is_array($touchMap)) {
            $touchMap = [];
        }

        $throttleSeconds = 60;
        $nowTs = time();
        $lastTouchedAt = (int)($touchMap[$userId] ?? 0);

        if ($lastTouchedAt > 0 && ($nowTs - $lastTouchedAt) < $throttleSeconds) {
            return;
        }

        $nowSql = date('Y-m-d H:i:s');

        $st = $pdo->prepare("UPDATE users SET last_seen_at = ? WHERE id = ? LIMIT 1");
        $st->execute([$nowSql, $userId]);

        $touchMap[$userId] = $nowTs;
        $_SESSION[$sessionKey] = $touchMap;

        if (isset($GLOBALS['me']) && is_array($GLOBALS['me'])) {
            $GLOBALS['me']['last_seen_at'] = $nowSql;
        }
    }
}

/* ===== language detect (strict: uk|ru|en) ===== */
$allowedLangs = start_allowed_langs($cfg);
$defaultLang  = start_default_lang($cfg, $allowedLangs);

$langSource = 'default';
$lang = '';

$getLangPresent = array_key_exists('lang', $_GET);
$getLang = $getLangPresent ? (string)($_GET['lang'] ?? '') : '';

if ($getLangPresent) {
    $lang = $getLang;
    $langSource = 'get';
} else {
    $cookieLang = (string)($_COOKIE['lang'] ?? '');
    if ($cookieLang !== '') {
        $lang = $cookieLang;
        $langSource = 'cookie';
    } else {
        $sessLang = (string)($_SESSION['lang'] ?? '');
        if ($sessLang !== '') {
            $lang = $sessLang;
            $langSource = 'session';
        } else {
            $lang = $defaultLang;
            $langSource = 'default';
        }
    }
}

$rawLang = strtolower(trim((string)$lang));
$lang = start_normalize_lang($rawLang, $allowedLangs, $defaultLang);

if ($langSource === 'get' && !in_array($rawLang, $allowedLangs, true)) {
    $langSource = 'get_invalid';
}

if (function_exists('set_lang')) {
    set_lang($lang);
} else {
    $GLOBALS['_APP_LANG'] = $lang;
}

$_SESSION['lang'] = function_exists('lang') ? lang() : $lang;
start_set_lang_cookie((string)$_SESSION['lang']);

$GLOBALS['_LANG_SOURCE'] = $langSource;

/* ===== clean redirect: прибираємо ?lang=... з URL =====
   Робимо тільки для GET, не для AJAX, не для /set_lang.php
*/
$method = strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));
if (
    $getLangPresent &&
    !$isAjax &&
    $method === 'GET' &&
    $reqPath !== '/set_lang.php' &&
    !headers_sent()
) {
    $cleanUrl = start_clean_lang_url($reqUri);
    if ($cleanUrl === '') $cleanUrl = '/';
    header('Location: ' . $cleanUrl, true, 301);
    exit;
}

$siteName = (string)($cfg['app']['site_name'] ?? 'VMEREGI');

/* ===== DB + SEO runtime ===== */
$pdo = db();

$seoRuntime = __DIR__ . '/seo_runtime.php';
if (
    !$isAdmin &&
    !$isFunctional &&
    !$isAjax &&
    PHP_SAPI !== 'cli' &&
    is_file($seoRuntime)
) {
    require_once $seoRuntime;

    if (function_exists('seo_rt_boot')) {
        try {
            seo_rt_boot();
        } catch (Throwable $e) {
            // SEO runtime не має ламати сайт
        }
    }
}
unset($seoRuntime);

/* ===== auth ===== */
$me  = current_user();

/* ===== account restrictions ===== */
try {
    if (is_array($me) && !empty($me['id'])) {
        $arState = ar_get_state($pdo, (int)$me['id']);
        $GLOBALS['_ACCOUNT_RESTRICTION_STATE'] = $arState;

        if (!empty($arState['has_any'])) {
            $GLOBALS['_ACCOUNT_RESTRICTION_NOTICE'] = ar_build_notice($arState);
        }

        if (ar_should_redirect_permanent_ban($arState, $reqPath, $isAjax, (string)($_SERVER['REQUEST_METHOD'] ?? 'GET'))) {
            if (function_exists('flash_set')) {
                flash_set('err', ar_action_error($arState, 'posts'));
            }
            header('Location: /profile/?ban_notice=1');
            exit;
        }
    } else {
        $GLOBALS['_ACCOUNT_RESTRICTION_STATE'] = null;
        $GLOBALS['_ACCOUNT_RESTRICTION_NOTICE'] = null;
    }
} catch (Throwable $e) {
    $GLOBALS['_ACCOUNT_RESTRICTION_STATE'] = null;
    $GLOBALS['_ACCOUNT_RESTRICTION_NOTICE'] = null;
}

/* ===== update online activity ===== */
try {
    if (PHP_SAPI !== 'cli') {
        start_touch_user_last_seen($pdo, $me);
    }
} catch (Throwable $e) {
    // last_seen не має ламати сайт
}

if (!isset($title)) $title = $siteName;

/* ===== traffic logging ===== */
require_once __DIR__ . '/traffic_log.php';

try {
    $trafficEnabled = defined('TRAFFIC_LOG_ENABLED') ? (bool)TRAFFIC_LOG_ENABLED : true;

    $method = strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));
    $isWriteMethod = !in_array($method, ['GET', 'HEAD'], true);

    $skipTraffic =
        $isWriteMethod ||
        $isAjax ||
        $isAdmin ||
        $isFunctional ||
        (PHP_SAPI === 'cli');

    if ($trafficEnabled && !$skipTraffic) {
        traffic_log_request($pdo, $me);
    }
} catch (Throwable $e) {
    // трафік не має ламати сайт
}