<?php
declare(strict_types=1);

if (defined('VM_SEO_ADMIN_LIB_LOADED')) {
    return;
}
define('VM_SEO_ADMIN_LIB_LOADED', true);

function seo_admin_db(): PDO
{
    if (function_exists('db')) {
        return db();
    }

    if (isset($GLOBALS['pdo']) && $GLOBALS['pdo'] instanceof PDO) {
        return $GLOBALS['pdo'];
    }

    throw new RuntimeException('db() not found');
}

function seo_admin_me(): ?array
{
    if (function_exists('current_user')) {
        $u = current_user();
        return is_array($u) ? $u : null;
    }

    return (isset($GLOBALS['me']) && is_array($GLOBALS['me'])) ? $GLOBALS['me'] : null;
}

function seo_admin_lang(): string
{
    $lang = (string)($_SESSION['lang'] ?? ((seo_admin_me()['lang'] ?? 'uk')));
    return in_array($lang, ['uk', 'ru', 'en'], true) ? $lang : 'uk';
}

function seo_admin_t(string $uk, string $ru, string $en): string
{
    $lang = seo_admin_lang();
    if ($lang === 'ru') return $ru;
    if ($lang === 'en') return $en;
    return $uk;
}

function seo_admin_h(?string $s): string
{
    if (function_exists('h')) {
        return h($s);
    }

    return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function seo_admin_url(string $page = 'index.php', array $query = []): string
{
    $page = trim($page);
    if ($page === '') $page = 'index.php';
    if ($page[0] === '/') $page = ltrim($page, '/');

    $url = '/admin/seo/' . $page;
    if ($query) {
        $url .= '?' . http_build_query($query);
    }
    return $url;
}

function seo_admin_csrf_token_local(): string
{
    if (function_exists('csrf_token')) {
        return (string)csrf_token();
    }

    if (session_status() !== PHP_SESSION_ACTIVE) {
        @session_start();
    }

    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(16));
    }

    return (string)$_SESSION['csrf_token'];
}

function seo_admin_csrf_check_local(?string $token): bool
{
    if (function_exists('csrf_check')) {
        return (bool)csrf_check($token);
    }

    $sess = (string)($_SESSION['csrf_token'] ?? '');
    $sent = (string)($token ?? '');
    return $sess !== '' && hash_equals($sess, $sent);
}

function seo_admin_set_flash(string $type, string $message): void
{
    $_SESSION['seo_admin_flash'] = [
        'type' => $type,
        'message' => $message,
    ];
}

function seo_admin_get_flash(): ?array
{
    $flash = $_SESSION['seo_admin_flash'] ?? null;
    unset($_SESSION['seo_admin_flash']);
    return is_array($flash) ? $flash : null;
}

function seo_admin_ident_ok(string $name): bool
{
    return (bool)preg_match('/^[a-zA-Z0-9_]+$/', $name);
}

function seo_admin_table_exists(PDO $pdo, string $table): bool
{
    static $cache = [];

    if (!seo_admin_ident_ok($table)) return false;
    if (array_key_exists($table, $cache)) return (bool)$cache[$table];

    if (function_exists('db_has_table')) {
        try {
            $cache[$table] = (bool)db_has_table($pdo, $table);
            return (bool)$cache[$table];
        } catch (Throwable $e) {
        }
    }

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

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

function seo_admin_column_exists(PDO $pdo, string $table, string $column): bool
{
    static $cache = [];

    if (!seo_admin_ident_ok($table) || !seo_admin_ident_ok($column)) return false;
    $k = $table . '.' . $column;
    if (array_key_exists($k, $cache)) return (bool)$cache[$k];

    if (function_exists('db_has_column')) {
        try {
            $cache[$k] = (bool)db_has_column($pdo, $table, $column);
            return (bool)$cache[$k];
        } catch (Throwable $e) {
        }
    }

    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];
}

function seo_admin_safe_int(PDO $pdo, string $sql, array $params = []): int
{
    try {
        $st = $pdo->prepare($sql);
        $st->execute($params);
        return (int)$st->fetchColumn();
    } catch (Throwable $e) {
        return 0;
    }
}

function seo_admin_safe_row(PDO $pdo, string $sql, array $params = []): array
{
    try {
        $st = $pdo->prepare($sql);
        $st->execute($params);
        $row = $st->fetch(PDO::FETCH_ASSOC);
        return is_array($row) ? $row : [];
    } catch (Throwable $e) {
        return [];
    }
}

function seo_admin_base_url(): string
{
    $cfg = $GLOBALS['cfg'] ?? null;
    if (is_array($cfg) && !empty($cfg['app']['base_url']) && is_string($cfg['app']['base_url'])) {
        return rtrim((string)$cfg['app']['base_url'], '/');
    }

    $host = (string)($_SERVER['HTTP_HOST'] ?? '');
    if ($host === '') return '';

    $https = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
    return ($https ? 'https://' : 'http://') . $host;
}

function seo_admin_root_path(): string
{
    return dirname(__DIR__, 2);
}

function seo_admin_robots_path(): string
{
    return seo_admin_root_path() . '/robots.txt';
}

function seo_admin_default_robots_text(): string
{
    $base = seo_admin_base_url();
    $sitemapUrl = ($base !== '' ? $base : '') . '/sitemap.xml.php';

    return implode("\n", [
        'User-agent: *',
        'Disallow: /admin/',
        'Disallow: /profile/setting/',
        'Disallow: /messages/',
        'Disallow: /login.php',
        'Disallow: /register.php',
        'Disallow: /functional/',
        'Allow: /',
        '',
        'Sitemap: ' . $sitemapUrl,
        '',
    ]);
}

function seo_admin_read_robots(): string
{
    $path = seo_admin_robots_path();
    if (!is_file($path)) {
        return seo_admin_default_robots_text();
    }

    $raw = @file_get_contents($path);
    if ($raw === false) {
        return seo_admin_default_robots_text();
    }

    return str_replace(["\r\n", "\r"], "\n", $raw);
}

function seo_admin_write_robots(string $content): void
{
    $path = seo_admin_robots_path();
    $content = str_replace(["\r\n", "\r"], "\n", trim($content)) . "\n";

    if (@file_put_contents($path, $content, LOCK_EX) === false) {
        throw new RuntimeException('Не вдалося записати robots.txt');
    }
}

function seo_admin_settings_defaults(): array
{
    return [
        'seo_enabled'                        => '1',
        'seo_index_profiles'                 => '1',
        'seo_index_groups'                   => '1',
        'seo_index_posts'                    => '1',
        'seo_index_albums'                   => '1',
        'seo_enable_canonical'               => '1',
        'seo_enable_og'                      => '1',
        'seo_enable_twitter'                 => '1',
        'seo_enable_hreflang'                => '1',
        'seo_enable_schema'                  => '1',
        'seo_noindex_private_profiles'       => '1',
        'seo_noindex_messages'               => '1',
        'seo_noindex_settings'               => '1',
        'seo_noindex_admin'                  => '1',
        'seo_default_title'                  => 'VMEREGI',
        'seo_title_suffix'                   => '— VMEREGI',
        'seo_default_description'            => 'Соціальна мережа VMEREGI.',
        'seo_default_og_image'               => '',
        'seo_profile_title_tpl'              => '{display_name} — Профіль',
        'seo_profile_description_tpl'        => '{display_name} (@{username}) на VMEREGI.',
        'seo_group_title_tpl'                => '{group_name} — Група',
        'seo_group_description_tpl'          => '{group_name} — спільнота на VMEREGI.',
        'seo_post_title_tpl'                 => 'Пост від {author_name}',
        'seo_post_description_tpl'           => '{post_excerpt}',
        'seo_album_title_tpl'                => '{album_title} — Фотоальбом',
        'seo_album_description_tpl'          => 'Фотоальбом користувача {display_name}',
        'seo_sitemap_include_profiles'       => '1',
        'seo_sitemap_include_groups'         => '1',
        'seo_sitemap_include_posts'          => '1',
        'seo_sitemap_include_albums'         => '1',
        'seo_sitemap_include_static'         => '1',
        'seo_sitemap_exclude_private_profiles'=> '1',
        'seo_sitemap_exclude_closed_groups'  => '0',
        'seo_sitemap_max_urls'               => '50000',
        'seo_sitemap_changefreq'             => 'daily',
        'seo_sitemap_priority_home'          => '1.0',
        'seo_sitemap_priority_profile'       => '0.8',
        'seo_sitemap_priority_group'         => '0.8',
        'seo_sitemap_priority_post'          => '0.7',
        'seo_sitemap_priority_album'         => '0.7',
        'seo_schema_org_name'                => '',
        'seo_schema_org_logo'                => '',
    ];
}

function seo_admin_load_settings(PDO $pdo): array
{
    $defaults = seo_admin_settings_defaults();

    if (!seo_admin_table_exists($pdo, 'app_settings')) {
        return $defaults;
    }

    try {
        $keys = array_keys($defaults);
        $in   = implode(',', array_fill(0, count($keys), '?'));

        $st = $pdo->prepare("SELECT key_name, value_text FROM app_settings WHERE key_name IN ($in)");
        $st->execute($keys);

        $map = $defaults;
        foreach (($st->fetchAll(PDO::FETCH_ASSOC) ?: []) as $row) {
            $k = (string)($row['key_name'] ?? '');
            if ($k !== '' && array_key_exists($k, $map)) {
                $map[$k] = (string)($row['value_text'] ?? '');
            }
        }

        return $map;
    } catch (Throwable $e) {
        return $defaults;
    }
}

function seo_admin_save_settings(PDO $pdo, array $values): void
{
    if (!seo_admin_table_exists($pdo, 'app_settings')) {
        throw new RuntimeException('Таблиця app_settings відсутня');
    }

    $allowed = seo_admin_settings_defaults();
    $payload = [];

    foreach ($allowed as $key => $default) {
        if (array_key_exists($key, $values)) {
            $payload[$key] = (string)$values[$key];
        }
    }

    if (!$payload) return;

    $ownTx = !$pdo->inTransaction();
    if ($ownTx) $pdo->beginTransaction();

    try {
        $st = $pdo->prepare(
            'INSERT INTO app_settings (key_name, value_text, updated_at) VALUES (?, ?, NOW()) '
            . 'ON DUPLICATE KEY UPDATE value_text = VALUES(value_text), updated_at = VALUES(updated_at)'
        );

        foreach ($payload as $k => $v) {
            $st->execute([$k, $v]);
        }

        if ($ownTx) $pdo->commit();
    } catch (Throwable $e) {
        if ($ownTx && $pdo->inTransaction()) $pdo->rollBack();
        throw $e;
    }
}

function seo_admin_bool_from_post(string $name): string
{
    return isset($_POST[$name]) ? '1' : '0';
}

function seo_admin_clean_path(string $path): string
{
    $path = trim(str_replace('\\', '/', $path));
    if ($path === '') return '/';
    if (!str_starts_with($path, '/')) $path = '/' . $path;
    $path = preg_replace('~/+~', '/', $path) ?: $path;
    return $path;
}

function seo_admin_dashboard_stats(PDO $pdo): array
{
    $stats = [
        'users_total'      => 0,
        'users_public'     => 0,
        'users_private'    => 0,
        'groups_total'     => 0,
        'groups_open'      => 0,
        'groups_closed'    => 0,
        'posts_total'      => 0,
        'albums_total'     => 0,
        'redirects_total'  => 0,
        'redirects_active' => 0,
        'pages_total'      => 0,
        'pages_active'     => 0,
    ];

    if (seo_admin_table_exists($pdo, 'users')) {
        $stats['users_total']   = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM users');
        if (seo_admin_column_exists($pdo, 'users', 'is_private')) {
            $stats['users_private'] = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM users WHERE is_private = 1');
            $stats['users_public']  = max(0, $stats['users_total'] - $stats['users_private']);
        } else {
            $stats['users_public'] = $stats['users_total'];
        }
    }

    if (seo_admin_table_exists($pdo, 'groups')) {
        $stats['groups_total'] = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM `groups`');
        if (seo_admin_column_exists($pdo, 'groups', 'is_closed')) {
            $stats['groups_closed'] = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM `groups` WHERE is_closed = 1');
            $stats['groups_open']   = max(0, $stats['groups_total'] - $stats['groups_closed']);
        } else {
            $stats['groups_open'] = $stats['groups_total'];
        }
    }

    if (seo_admin_table_exists($pdo, 'posts')) {
        $stats['posts_total'] = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM posts');
    }

    if (seo_admin_table_exists($pdo, 'photo_albums')) {
        $stats['albums_total'] = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM photo_albums');
    }

    if (seo_admin_table_exists($pdo, 'seo_redirects')) {
        $stats['redirects_total']  = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM seo_redirects');
        $stats['redirects_active'] = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM seo_redirects WHERE is_active = 1');
    }

    if (seo_admin_table_exists($pdo, 'seo_pages')) {
        $stats['pages_total']  = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM seo_pages');
        $stats['pages_active'] = seo_admin_safe_int($pdo, 'SELECT COUNT(*) FROM seo_pages WHERE is_active = 1');
    }

    return $stats;
}

function seo_admin_redirects(PDO $pdo, int $limit = 100): array
{
    if (!seo_admin_table_exists($pdo, 'seo_redirects')) return [];
    $limit = max(1, min(500, $limit));

    try {
        $st = $pdo->query('SELECT * FROM seo_redirects ORDER BY is_active DESC, id DESC LIMIT ' . (int)$limit);
        return $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
    } catch (Throwable $e) {
        return [];
    }
}

function seo_admin_get_redirect(PDO $pdo, int $id): ?array
{
    if ($id <= 0 || !seo_admin_table_exists($pdo, 'seo_redirects')) return null;
    $row = seo_admin_safe_row($pdo, 'SELECT * FROM seo_redirects WHERE id = ? LIMIT 1', [$id]);
    return $row ?: null;
}

function seo_admin_save_redirect(PDO $pdo, array $data, int $adminUserId = 0, int $id = 0): int
{
    if (!seo_admin_table_exists($pdo, 'seo_redirects')) {
        throw new RuntimeException('Таблиця seo_redirects відсутня. Спочатку виконай SQL-міграцію.');
    }

    $oldPath   = seo_admin_clean_path((string)($data['old_path'] ?? '/'));
    $targetUrl = trim((string)($data['target_url'] ?? ''));
    $httpCode  = (int)($data['http_code'] ?? 301);
    $isActive  = !empty($data['is_active']) ? 1 : 0;
    $note      = trim((string)($data['note'] ?? ''));

    if ($targetUrl === '') {
        throw new RuntimeException('Вкажи цільовий URL для редіректу.');
    }

    if (!in_array($httpCode, [301, 302], true)) {
        $httpCode = 301;
    }

    if ($id > 0) {
        $st = $pdo->prepare('UPDATE seo_redirects SET old_path=?, target_url=?, http_code=?, is_active=?, note=?, updated_by_user_id=?, updated_at=NOW() WHERE id=?');
        $st->execute([$oldPath, $targetUrl, $httpCode, $isActive, $note, $adminUserId ?: null, $id]);
        return $id;
    }

    $st = $pdo->prepare('INSERT INTO seo_redirects (old_path, target_url, http_code, is_active, note, created_by_user_id, updated_by_user_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())');
    $st->execute([$oldPath, $targetUrl, $httpCode, $isActive, $note, $adminUserId ?: null, $adminUserId ?: null]);
    return (int)$pdo->lastInsertId();
}

function seo_admin_delete_redirect(PDO $pdo, int $id): void
{
    if ($id <= 0 || !seo_admin_table_exists($pdo, 'seo_redirects')) return;
    $st = $pdo->prepare('DELETE FROM seo_redirects WHERE id = ?');
    $st->execute([$id]);
}

function seo_admin_pages(PDO $pdo, int $limit = 100): array
{
    if (!seo_admin_table_exists($pdo, 'seo_pages')) return [];
    $limit = max(1, min(500, $limit));

    try {
        $st = $pdo->query('SELECT * FROM seo_pages ORDER BY is_active DESC, id DESC LIMIT ' . (int)$limit);
        return $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
    } catch (Throwable $e) {
        return [];
    }
}

function seo_admin_get_page(PDO $pdo, int $id): ?array
{
    if ($id <= 0 || !seo_admin_table_exists($pdo, 'seo_pages')) return null;
    $row = seo_admin_safe_row($pdo, 'SELECT * FROM seo_pages WHERE id = ? LIMIT 1', [$id]);
    return $row ?: null;
}

function seo_admin_save_page(PDO $pdo, array $data, int $adminUserId = 0, int $id = 0): int
{
    if (!seo_admin_table_exists($pdo, 'seo_pages')) {
        throw new RuntimeException('Таблиця seo_pages відсутня. Спочатку виконай SQL-міграцію.');
    }

    $pagePath         = seo_admin_clean_path((string)($data['page_path'] ?? '/'));
    $pageTitle        = trim((string)($data['page_title'] ?? ''));
    $metaDescription  = trim((string)($data['meta_description'] ?? ''));
    $canonicalUrl     = trim((string)($data['canonical_url'] ?? ''));
    $ogTitle          = trim((string)($data['og_title'] ?? ''));
    $ogDescription    = trim((string)($data['og_description'] ?? ''));
    $ogImage          = trim((string)($data['og_image'] ?? ''));
    $robotsDirective  = trim((string)($data['robots_directive'] ?? 'index,follow'));
    $isActive         = !empty($data['is_active']) ? 1 : 0;

    if ($id > 0) {
        $st = $pdo->prepare('UPDATE seo_pages SET page_path=?, page_title=?, meta_description=?, canonical_url=?, og_title=?, og_description=?, og_image=?, robots_directive=?, is_active=?, updated_by_user_id=?, updated_at=NOW() WHERE id=?');
        $st->execute([$pagePath, $pageTitle, $metaDescription, $canonicalUrl, $ogTitle, $ogDescription, $ogImage, $robotsDirective, $isActive, $adminUserId ?: null, $id]);
        return $id;
    }

    $st = $pdo->prepare('INSERT INTO seo_pages (page_path, page_title, meta_description, canonical_url, og_title, og_description, og_image, robots_directive, is_active, created_by_user_id, updated_by_user_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())');
    $st->execute([$pagePath, $pageTitle, $metaDescription, $canonicalUrl, $ogTitle, $ogDescription, $ogImage, $robotsDirective, $isActive, $adminUserId ?: null, $adminUserId ?: null]);
    return (int)$pdo->lastInsertId();
}

function seo_admin_delete_page(PDO $pdo, int $id): void
{
    if ($id <= 0 || !seo_admin_table_exists($pdo, 'seo_pages')) return;
    $st = $pdo->prepare('DELETE FROM seo_pages WHERE id = ?');
    $st->execute([$id]);
}

function seo_admin_add_log(PDO $pdo, int $adminUserId, string $entityType, ?int $entityId, string $actionKey, array $oldValue = [], array $newValue = []): void
{
    if (!seo_admin_table_exists($pdo, 'seo_change_log')) return;

    try {
        $st = $pdo->prepare('INSERT INTO seo_change_log (admin_user_id, entity_type, entity_id, action_key, old_value_json, new_value_json, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW())');
        $st->execute([
            $adminUserId > 0 ? $adminUserId : null,
            $entityType,
            $entityId,
            $actionKey,
            json_encode($oldValue, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            json_encode($newValue, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
    } catch (Throwable $e) {
    }
}

function seo_admin_recent_logs(PDO $pdo, int $limit = 20): array
{
    if (!seo_admin_table_exists($pdo, 'seo_change_log')) return [];
    $limit = max(1, min(100, $limit));

    try {
        $st = $pdo->query('SELECT * FROM seo_change_log ORDER BY id DESC LIMIT ' . (int)$limit);
        return $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
    } catch (Throwable $e) {
        return [];
    }
}

function seo_admin_yes_no_badge(bool $ok): string
{
    $text = $ok
        ? seo_admin_t('Увімкнено', 'Включено', 'Enabled')
        : seo_admin_t('Вимкнено', 'Выключено', 'Disabled');
    $cls = $ok ? 'success' : 'secondary';
    return '<span class="badge bg-' . $cls . '">' . seo_admin_h($text) . '</span>';
}

function seo_admin_file_badge(bool $ok): string
{
    $text = $ok
        ? seo_admin_t('Є файл', 'Файл есть', 'File exists')
        : seo_admin_t('Немає файла', 'Файла нет', 'File missing');
    $cls = $ok ? 'success' : 'warning';
    return '<span class="badge bg-' . $cls . '">' . seo_admin_h($text) . '</span>';
}

function seo_admin_module_table_state(PDO $pdo): array
{
    return [
        'app_settings'   => seo_admin_table_exists($pdo, 'app_settings'),
        'seo_redirects'  => seo_admin_table_exists($pdo, 'seo_redirects'),
        'seo_pages'      => seo_admin_table_exists($pdo, 'seo_pages'),
        'seo_change_log' => seo_admin_table_exists($pdo, 'seo_change_log'),
    ];
}
