<?php
declare(strict_types=1);

/**
 * /index.php
 *
 * Відповідає за:
 * - головну стрічку сайту
 * - SEO для стрічки / гостьової головної
 * - завантаження постів, репостів, вкладень, опитувань, коментарів і превʼю посилань
 * - AJAX-реакції на пости й коментарі
 * - показ promoted post у стрічці
 * - підключення окремих бокових рекламних блоків:
 *   - /blocks/reclama-left.php
 *   - /blocks/reclama-right.php
 */

require_once __DIR__ . '/includes/start.php';

$reclamaLib = __DIR__ . '/functional/reclama/_lib.php';
if (is_file($reclamaLib)) {
    require_once $reclamaLib;
}

$friendsLib = __DIR__ . '/functional/friends/_lib.php';
if (is_file($friendsLib)) {
    require_once $friendsLib;
}

require_once __DIR__ . '/includes/feed_helpers.php';

$pdo = (isset($pdo) && ($pdo instanceof PDO)) ? $pdo : db();
$me  = $me ?? current_user();

$title = t('feed');

$meId = (int)($me['id'] ?? 0);
$isAdmin = is_admin($me);

$lang = (string)lang();
if (!in_array($lang, ['uk','ru','en'], true)) $lang = 'uk';

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

$vmEmojiI18n = [
    'searchPh'     => $T('Пошук емодзі...', 'Поиск эмодзи...', 'Search emoji...'),
    'recentEmpty'  => $T('Нещодавніх емодзі ще немає', 'Недавних эмодзи ещё нет', 'No recent emoji yet'),
    'tabRecent'    => $T('Нещодавні', 'Недавние', 'Recent'),
    'tabSmileys'   => $T('Смайли', 'Смайлы', 'Smileys'),
    'tabGestures'  => $T('Жести', 'Жесты', 'Gestures'),
    'tabLove'      => $T('Любов', 'Любовь', 'Love'),
    'tabFood'      => $T('Їжа', 'Еда', 'Food'),
    'tabObjects'   => $T('Обʼєкти', 'Объекты', 'Objects'),
];

/* =========================
   SEO
========================= */
$isGuest = !$me;

$siteName = function_exists('t') ? (string)t('site') : 'VMEREGI';
$meta_desc = $isGuest
    ? $T(
        'VMEREGI — сучасна соціальна мережа. Друзі, групи, пости, музика та спілкування.',
        'VMEREGI — современная социальная сеть. Друзья, группы, посты, музыка и общение.',
        'VMEREGI — a modern social network. Friends, groups, posts and messaging.'
    )
    : $T(
        'Стрічка новин VMEREGI — пости друзів і груп.',
        'Лента новостей VMEREGI — посты друзей и групп.',
        'VMEREGI feed — posts from friends and groups.'
    );

$canonical = abs_url('/');
$og_image = $og_image ?? '';
$seo_noindex = !$isGuest;

require __DIR__ . '/includes/header.php';

$hasRepost  = vm_has_col($pdo, 'posts', 'repost_post_id');
$hasGroupId = vm_has_col($pdo, 'posts', 'group_id');

$groupHasName   = $hasGroupId ? vm_has_col($pdo, 'groups', 'name') : false;
$groupHasSlug   = $hasGroupId ? vm_has_col($pdo, 'groups', 'slug') : false;
$groupHasAva    = $hasGroupId ? vm_has_col($pdo, 'groups', 'avatar_path') : false;
$groupHasOwner  = $hasGroupId ? vm_has_col($pdo, 'groups', 'owner_user_id') : false;
$groupHasClosed = $hasGroupId ? vm_has_col($pdo, 'groups', 'is_closed') : false;

$gmHasRole     = $hasGroupId ? vm_has_col($pdo, 'group_members', 'role') : false;
$gmHasStatus   = $hasGroupId ? vm_has_col($pdo, 'group_members', 'status') : false;

$usersHasFullName = vm_has_col($pdo, 'users', 'full_name');
$usersHasPrivate  = vm_has_col($pdo, 'users', 'is_private');

$hasFriendRequests = false;
if ($usersHasPrivate) {
    if (function_exists('vm_table_exists')) {
        try {
            $hasFriendRequests = vm_table_exists($pdo, 'friend_requests');
        } catch (Throwable $e) {
            $hasFriendRequests = false;
        }
    } else {
        try {
            $stTbl = $pdo->prepare("
              SELECT 1
              FROM information_schema.tables
              WHERE table_schema = DATABASE() AND table_name = ?
              LIMIT 1
            " );
            $stTbl->execute(['friend_requests']);
            $hasFriendRequests = (bool)$stTbl->fetchColumn();
        } catch (Throwable $e) {
            $hasFriendRequests = false;
        }
    }
}

/* ===== build group/profile visibility filters for feed ===== */
$whereParts = [];
$whereParams = [];

if ($hasGroupId) {
    if ($meId <= 0) {
        $gOpenCond = $groupHasClosed ? "(g0.is_closed=0 OR g0.is_closed IS NULL)" : "1=1";
        $whereParts[] = "(
          (p.group_id IS NULL OR p.group_id=0)
          OR EXISTS (
            SELECT 1 FROM `groups` g0
            WHERE g0.id=p.group_id AND {$gOpenCond}
            LIMIT 1
          )
        )";

        if ($hasRepost) {
            $gOpenCond2 = $groupHasClosed ? "(g2.is_closed=0 OR g2.is_closed IS NULL)" : "1=1";
            $whereParts[] = "(
              (op.group_id IS NULL OR op.group_id=0)
              OR EXISTS (
                SELECT 1 FROM `groups` g2
                WHERE g2.id=op.group_id AND {$gOpenCond2}
                LIMIT 1
              )
            )";
        }
    } elseif ($isAdmin) {
        // admin sees all
    } else {
        $memberStatusSql = $gmHasStatus ? " AND gm1.status='active' " : " ";
        $ownerSql = $groupHasOwner
            ? " OR EXISTS (SELECT 1 FROM `groups` g1 WHERE g1.id=p.group_id AND g1.owner_user_id=? LIMIT 1) "
            : " ";

        $whereParts[] = "(
          (p.group_id IS NULL OR p.group_id=0)
          OR EXISTS (
            SELECT 1 FROM group_members gm1
            WHERE gm1.group_id=p.group_id AND gm1.user_id=? {$memberStatusSql}
            LIMIT 1
          )
          {$ownerSql}
        )";
        $whereParams[] = $meId;
        if ($groupHasOwner) $whereParams[] = $meId;

        if ($hasRepost) {
            $memberStatusSql2 = $gmHasStatus ? " AND gm2.status='active' " : " ";
            $ownerSql2 = $groupHasOwner
                ? " OR EXISTS (SELECT 1 FROM `groups` g2 WHERE g2.id=op.group_id AND g2.owner_user_id=? LIMIT 1) "
                : " ";

            $whereParts[] = "(
              (op.group_id IS NULL OR op.group_id=0)
              OR EXISTS (
                SELECT 1 FROM group_members gm2
                WHERE gm2.group_id=op.group_id AND gm2.user_id=? {$memberStatusSql2}
                LIMIT 1
              )
              {$ownerSql2}
            )";
            $whereParams[] = $meId;
            if ($groupHasOwner) $whereParams[] = $meId;
        }
    }
}

if ($usersHasPrivate && !$isAdmin) {
    if ($meId <= 0) {
        $whereParts[] = "(COALESCE(u.is_private, 0) = 0)";
        if ($hasRepost) {
            $whereParts[] = "(op.id IS NULL OR COALESCE(ou.is_private, 0) = 0)";
        }
    } else {
        $friendSqlP = '';
        if ($hasFriendRequests) {
            $friendSqlP = "
              OR EXISTS (
                SELECT 1
                FROM friend_requests frp
                WHERE frp.status='accepted'
                  AND (
                    (frp.from_user_id = p.user_id AND frp.to_user_id = ?)
                    OR (frp.from_user_id = ? AND frp.to_user_id = p.user_id)
                  )
                LIMIT 1
              )
            ";
        }

        $whereParts[] = "(
          COALESCE(u.is_private, 0) = 0
          OR p.user_id = ?
          {$friendSqlP}
        )";
        $whereParams[] = $meId;
        if ($hasFriendRequests) {
            $whereParams[] = $meId;
            $whereParams[] = $meId;
        }

        if ($hasRepost) {
            $friendSqlOp = '';
            if ($hasFriendRequests) {
                $friendSqlOp = "
                  OR EXISTS (
                    SELECT 1
                    FROM friend_requests fro
                    WHERE fro.status='accepted'
                      AND (
                        (fro.from_user_id = op.user_id AND fro.to_user_id = ?)
                        OR (fro.from_user_id = ? AND fro.to_user_id = op.user_id)
                      )
                    LIMIT 1
                  )
                ";
            }

            $whereParts[] = "(
              op.id IS NULL
              OR COALESCE(ou.is_private, 0) = 0
              OR op.user_id = ?
              {$friendSqlOp}
            )";
            $whereParams[] = $meId;
            if ($hasFriendRequests) {
                $whereParams[] = $meId;
                $whereParams[] = $meId;
            }
        }
    }
}

$whereSql = $whereParts ? ("WHERE " . implode(" AND ", $whereParts)) : "";

/* ===== load posts ===== */
$params = [];
$sql = "";

$uFullNameSel  = $usersHasFullName ? "u.full_name" : "NULL";
$ouFullNameSel = $usersHasFullName ? "ou.full_name" : "NULL";

if ($hasRepost) {
    if ($hasGroupId) {
        $gNameSel  = $groupHasName ? "g.name" : "NULL";
        $gSlugSel  = $groupHasSlug ? "g.slug" : "NULL";
        $gAvaSel   = $groupHasAva ? "g.avatar_path" : "NULL";
        $gOwnerSel = $groupHasOwner ? "g.owner_user_id" : "0";

        $ogNameSel  = $groupHasName ? "og.name" : "NULL";
        $ogSlugSel  = $groupHasSlug ? "og.slug" : "NULL";
        $ogAvaSel   = $groupHasAva ? "og.avatar_path" : "NULL";
        $ogOwnerSel = $groupHasOwner ? "og.owner_user_id" : "0";

        $gmRoleSel   = $gmHasRole ? "gm.role" : "NULL";
        $gmStatusSel = $gmHasStatus ? "gm.status" : "NULL";

        $sql = "
          SELECT
            p.id, p.user_id, p.group_id, p.repost_post_id, p.body, p.created_at,
            u.username, u.first_name, u.last_name, u.avatar_path,
            {$uFullNameSel} AS full_name,

            g.id AS group_pk,
            {$gNameSel} AS group_name,
            {$gSlugSel} AS group_slug,
            {$gAvaSel} AS group_avatar_path,
            {$gOwnerSel} AS group_owner_user_id,
            {$gmRoleSel} AS my_group_role,
            {$gmStatusSel} AS my_group_status,

            op.id AS orig_id, op.user_id AS orig_user_id, op.group_id AS orig_group_id, op.body AS orig_body, op.created_at AS orig_created_at,
            ou.username AS orig_username, ou.first_name AS orig_first_name, ou.last_name AS orig_last_name, ou.avatar_path AS orig_avatar_path,
            {$ouFullNameSel} AS orig_full_name,

            og.id AS orig_group_pk,
            {$ogNameSel} AS orig_group_name,
            {$ogSlugSel} AS orig_group_slug,
            {$ogAvaSel} AS orig_group_avatar_path,
            {$ogOwnerSel} AS orig_group_owner_user_id
          FROM posts p
          JOIN users u ON u.id = p.user_id

          LEFT JOIN `groups` g ON g.id = p.group_id
          LEFT JOIN group_members gm ON gm.group_id = p.group_id AND gm.user_id = ?

          LEFT JOIN posts op ON op.id = p.repost_post_id
          LEFT JOIN users ou ON ou.id = op.user_id
          LEFT JOIN `groups` og ON og.id = op.group_id

          {$whereSql}

          ORDER BY p.id DESC
          LIMIT 30
        ";

        $params[] = $meId;
        $params = array_merge($params, $whereParams);
    } else {
        $sql = "
          SELECT
            p.id, p.user_id, p.repost_post_id, p.body, p.created_at,
            u.username, u.first_name, u.last_name, u.avatar_path,
            {$uFullNameSel} AS full_name,

            op.id AS orig_id, op.user_id AS orig_user_id, op.body AS orig_body, op.created_at AS orig_created_at,
            ou.username AS orig_username, ou.first_name AS orig_first_name, ou.last_name AS orig_last_name, ou.avatar_path AS orig_avatar_path,
            {$ouFullNameSel} AS orig_full_name
          FROM posts p
          JOIN users u ON u.id = p.user_id
          LEFT JOIN posts op ON op.id = p.repost_post_id
          LEFT JOIN users ou ON ou.id = op.user_id
          {$whereSql}
          ORDER BY p.id DESC
          LIMIT 30
        ";
        $params = $whereParams;
    }
} else {
    if ($hasGroupId) {
        $gNameSel  = $groupHasName ? "g.name" : "NULL";
        $gSlugSel  = $groupHasSlug ? "g.slug" : "NULL";
        $gAvaSel   = $groupHasAva ? "g.avatar_path" : "NULL";
        $gOwnerSel = $groupHasOwner ? "g.owner_user_id" : "0";

        $gmRoleSel   = $gmHasRole ? "gm.role" : "NULL";
        $gmStatusSel = $gmHasStatus ? "gm.status" : "NULL";

        $sql = "
          SELECT
            p.id, p.user_id, p.group_id, p.body, p.created_at,
            u.username, u.first_name, u.last_name, u.avatar_path,
            {$uFullNameSel} AS full_name,

            g.id AS group_pk,
            {$gNameSel} AS group_name,
            {$gSlugSel} AS group_slug,
            {$gAvaSel} AS group_avatar_path,
            {$gOwnerSel} AS group_owner_user_id,
            {$gmRoleSel} AS my_group_role,
            {$gmStatusSel} AS my_group_status
          FROM posts p
          JOIN users u ON u.id = p.user_id

          LEFT JOIN `groups` g ON g.id = p.group_id
          LEFT JOIN group_members gm ON gm.group_id = p.group_id AND gm.user_id = ?

          {$whereSql}

          ORDER BY p.id DESC
          LIMIT 30
        ";
        $params[] = $meId;
        $params = array_merge($params, $whereParams);
    } else {
        $sql = "
          SELECT
            p.id, p.user_id, p.body, p.created_at,
            u.username, u.first_name, u.last_name, u.avatar_path,
            {$uFullNameSel} AS full_name
          FROM posts p
          JOIN users u ON u.id = p.user_id
          {$whereSql}
          ORDER BY p.id DESC
          LIMIT 30
        ";
        $params = $whereParams;
    }
}

$st = $pdo->prepare($sql);
$st->execute($params);
$posts = $st->fetchAll() ?: [];

/* ===== stage 7: promoted post in feed + desktop side banners ===== */
$feedPromotedCampaign = null;
$sidebarBannerLeft = null;
$sidebarBannerRight = null;
$sidebarBannerLeftClickUrl = '';
$sidebarBannerRightClickUrl = '';

if (
    function_exists('reclama_is_enabled')
    && function_exists('reclama_viewer_context')
    && function_exists('reclama_select_feed_campaign')
    && function_exists('reclama_promoted_post_row')
    && function_exists('reclama_insert_promoted_into_feed')
    && reclama_is_enabled($pdo)
) {
    $reclamaViewer = reclama_viewer_context($pdo, $me, (string)($_SERVER['HTTP_USER_AGENT'] ?? ''));

    $organicPostIds = array_values(array_filter(array_map(
        static fn(array $row): int => (int)($row['id'] ?? 0),
        $posts
    ), static fn(int $id): bool => $id > 0));

    $feedPromotedCampaign = reclama_select_feed_campaign($pdo, $reclamaViewer, $organicPostIds, 'feed');
    if ($feedPromotedCampaign) {
        $promotedRow = reclama_promoted_post_row($pdo, $feedPromotedCampaign, $meId);
        if ($promotedRow) {
            $posts = reclama_insert_promoted_into_feed($posts, $promotedRow, reclama_feed_interval_posts($pdo));
            reclama_log_impression($pdo, (int)($feedPromotedCampaign['id'] ?? 0), $reclamaViewer, 'feed');
        }
    }

    if (function_exists('reclama_select_sidebar_campaigns') && function_exists('reclama_viewer_is_desktop') && reclama_viewer_is_desktop($reclamaViewer)) {
        $sidebarSelected = reclama_select_sidebar_campaigns($pdo, $reclamaViewer, 2, []);
        if (!empty($sidebarSelected[0])) {
            $sidebarBannerLeft = $sidebarSelected[0];
            reclama_log_impression($pdo, (int)($sidebarBannerLeft['id'] ?? 0), $reclamaViewer, 'other');
            $sidebarBannerLeftClickUrl = reclama_build_click_url(
                (int)($sidebarBannerLeft['id'] ?? 0),
                (string)($sidebarBannerLeft['target_url'] ?? '/'),
                'other'
            );
        }
        if (!empty($sidebarSelected[1])) {
            $sidebarBannerRight = $sidebarSelected[1];
            reclama_log_impression($pdo, (int)($sidebarBannerRight['id'] ?? 0), $reclamaViewer, 'other');
            $sidebarBannerRightClickUrl = reclama_build_click_url(
                (int)($sidebarBannerRight['id'] ?? 0),
                (string)($sidebarBannerRight['target_url'] ?? '/'),
                'other'
            );
        }
    }
}

/* ===== IDs for feed items ===== */
$postIds = array_map(fn($r) => (int)$r['id'], $posts);
$postIds = array_values(array_filter($postIds, fn($id) => $id > 0));

/* ===== load reactions for posts ===== */
$postLikesById = [];
$postDislikesById = [];
$myPostReactionById = [];

if ($postIds && vm_has_tbl($pdo, 'post_reactions')) {
    $in = implode(',', array_fill(0, count($postIds), '?'));

    $st = $pdo->prepare("
      SELECT post_id,
             SUM(reaction='like') AS likes,
             SUM(reaction='dislike') AS dislikes
      FROM post_reactions
      WHERE post_id IN ($in)
      GROUP BY post_id
    ");
    $st->execute($postIds);
    $rows = $st->fetchAll() ?: [];
    foreach ($rows as $r) {
        $pid = (int)$r['post_id'];
        $postLikesById[$pid] = (int)($r['likes'] ?? 0);
        $postDislikesById[$pid] = (int)($r['dislikes'] ?? 0);
    }

    if ($meId > 0) {
        $st = $pdo->prepare("SELECT post_id, reaction FROM post_reactions WHERE user_id=? AND post_id IN ($in)");
        $st->execute(array_merge([$meId], $postIds));

        $rows = $st->fetchAll() ?: [];
        foreach ($rows as $r) {
            $myPostReactionById[(int)$r['post_id']] = (string)($r['reaction'] ?? '');
        }
    }
}

if ($posts) {
    foreach ($posts as &$p) {
        $pid = (int)($p['id'] ?? 0);
        $p['likes'] = (int)($postLikesById[$pid] ?? 0);
        $p['dislikes'] = (int)($postDislikesById[$pid] ?? 0);
        $p['my_reaction'] = (string)($myPostReactionById[$pid] ?? '');
    }
    unset($p);
}

/* ===== content IDs ===== */
$contentIds = [];
if ($posts) {
    foreach ($posts as $p) {
        $pid = (int)($p['id'] ?? 0);
        if ($pid <= 0) continue;
        $cid = $pid;
        if ($hasRepost) {
            $rid = (int)($p['repost_post_id'] ?? 0);
            if ($rid > 0) $cid = $rid;
        }
        $contentIds[] = $cid;
    }
    $contentIds = array_values(array_unique(array_filter($contentIds, fn($id) => $id > 0)));
}

/* ===== attachments / polls vars ===== */
$attachmentsByPost = [];
$pollByPost = [];
$pollOptionsByPoll = [];
$pollVotesByOption = [];
$myPollVoteByPoll = [];

/* ===== comments vars ===== */
$commentsByPost = [];
$repliesByParent = [];
$commentAttById = [];
$commentIds = [];

/* ===== load post attachments ===== */
if ($contentIds && vm_has_tbl($pdo, 'post_attachments')) {
    $in = implode(',', array_fill(0, count($contentIds), '?'));
    $st = $pdo->prepare("
      SELECT id, post_id, file_type, path, mime, original_name
      FROM post_attachments
      WHERE post_id IN ($in)
      ORDER BY id ASC
    ");
    $st->execute($contentIds);
    $rows = $st->fetchAll() ?: [];
    foreach ($rows as $a) {
        $pid = (int)$a['post_id'];
        $attachmentsByPost[$pid][] = $a;
    }
}

/* ===== load polls ===== */
$pollIds = [];
if ($contentIds && vm_has_tbl($pdo, 'post_polls')) {
    $in = implode(',', array_fill(0, count($contentIds), '?'));
    $st = $pdo->prepare("SELECT id, post_id, question FROM post_polls WHERE post_id IN ($in)");
    $st->execute($contentIds);
    $rows = $st->fetchAll() ?: [];
    foreach ($rows as $p) {
        $pollByPost[(int)$p['post_id']] = $p;
        $pollIds[] = (int)$p['id'];
    }
}

if ($pollIds && vm_has_tbl($pdo, 'post_poll_options') && vm_has_tbl($pdo, 'post_poll_votes')) {
    $in = implode(',', array_fill(0, count($pollIds), '?'));

    $st = $pdo->prepare("
      SELECT id, poll_id, option_text, sort_order
      FROM post_poll_options
      WHERE poll_id IN ($in)
      ORDER BY sort_order ASC, id ASC
    ");
    $st->execute($pollIds);
    $opts = $st->fetchAll() ?: [];
    foreach ($opts as $o) {
        $pollOptionsByPoll[(int)$o['poll_id']][] = $o;
    }

    $st = $pdo->prepare("SELECT option_id, COUNT(*) AS cnt FROM post_poll_votes WHERE poll_id IN ($in) GROUP BY option_id");
    $st->execute($pollIds);
    $vc = $st->fetchAll() ?: [];
    foreach ($vc as $r) {
        $pollVotesByOption[(int)$r['option_id']] = (int)$r['cnt'];
    }

    if ($meId > 0) {
        $st = $pdo->prepare("SELECT poll_id, option_id FROM post_poll_votes WHERE user_id=? AND poll_id IN ($in)");
        $st->execute(array_merge([$meId], $pollIds));
        $mv = $st->fetchAll() ?: [];
        foreach ($mv as $r) {
            $myPollVoteByPoll[(int)$r['poll_id']] = (int)$r['option_id'];
        }
    }
}

/* ===== load comments ===== */
if ($postIds && vm_has_tbl($pdo, 'comments')) {
    $in = implode(',', array_fill(0, count($postIds), '?'));

    $cFullNameSel = $usersHasFullName ? "u.full_name" : "NULL";

    $sql = "
      SELECT
        c.id, c.post_id, c.user_id, c.parent_id, c.body, c.created_at,
        u.username, u.first_name, u.last_name, u.avatar_path,
        {$cFullNameSel} AS full_name
      FROM comments c
      JOIN users u ON u.id = c.user_id
      WHERE c.post_id IN ($in)
      ORDER BY c.created_at ASC, c.id ASC
    ";
    $st = $pdo->prepare($sql);
    $st->execute($postIds);
    $rows = $st->fetchAll() ?: [];

    $tmp = [];
    foreach ($rows as $c) {
        $cid = (int)($c['id'] ?? 0);
        if ($cid <= 0) continue;
        $commentIds[] = $cid;
        $tmp[$cid] = $c;
    }

    $cLikesById = [];
    $cDislikesById = [];
    $myCReactionById = [];

    if ($commentIds && vm_has_tbl($pdo, 'comment_reactions')) {
        $commentIds = array_values(array_unique($commentIds));
        $cin = implode(',', array_fill(0, count($commentIds), '?'));

        $st = $pdo->prepare("
          SELECT comment_id,
                 SUM(reaction='like') AS likes,
                 SUM(reaction='dislike') AS dislikes
          FROM comment_reactions
          WHERE comment_id IN ($cin)
          GROUP BY comment_id
        ");
        $st->execute($commentIds);
        $rc = $st->fetchAll() ?: [];
        foreach ($rc as $r) {
            $cid = (int)$r['comment_id'];
            $cLikesById[$cid] = (int)($r['likes'] ?? 0);
            $cDislikesById[$cid] = (int)($r['dislikes'] ?? 0);
        }

        if ($meId > 0) {
            $st = $pdo->prepare("SELECT comment_id, reaction FROM comment_reactions WHERE user_id=? AND comment_id IN ($cin)");
            $st->execute(array_merge([$meId], $commentIds));
            $mr = $st->fetchAll() ?: [];
            foreach ($mr as $r) {
                $myCReactionById[(int)$r['comment_id']] = (string)($r['reaction'] ?? '');
            }
        }
    } else {
        $commentIds = array_values(array_unique($commentIds));
    }

    foreach ($tmp as $cid => $c) {
        $c['likes'] = (int)($cLikesById[$cid] ?? 0);
        $c['dislikes'] = (int)($cDislikesById[$cid] ?? 0);
        $c['my_reaction'] = (string)($myCReactionById[$cid] ?? '');

        $pid = (int)($c['post_id'] ?? 0);
        $parent = (int)($c['parent_id'] ?? 0);

        if ($parent > 0) {
            $repliesByParent[$parent][] = $c;
        } else {
            $commentsByPost[$pid][] = $c;
        }
    }
}

/* ===== load comment attachments ===== */
if ($commentIds && vm_has_tbl($pdo, 'comment_attachments')) {
    $commentIds = array_values(array_unique($commentIds));
    $in = implode(',', array_fill(0, count($commentIds), '?'));


    $st = $pdo->prepare("
      SELECT id, comment_id, file_type, path, mime, original_name
      FROM comment_attachments
      WHERE comment_id IN ($in)
      ORDER BY id ASC
    ");
    $st->execute($commentIds);
    $rows = $st->fetchAll() ?: [];
    foreach ($rows as $a) {
        $cid = (int)$a['comment_id'];
        $commentAttById[$cid][] = $a;
    }
}

/* ===== link previews ===== */
$previewByHash = [];
$hashToUrl = [];

if (vm_has_tbl($pdo, 'link_previews')) {
    foreach ($posts as $p) {
        $txt = (string)($p['body'] ?? '');
        $u = extract_first_url($txt);
        $u = normalize_url($u);
        if ($u === '') continue;
        $hash = url_hash($u);
        $hashToUrl[$hash] = $u;
    }

    foreach ($commentsByPost as $items) {
        foreach ($items as $c) {
            $txt = (string)($c['body'] ?? '');
            $u = extract_first_url($txt);
            $u = normalize_url($u);
            if ($u === '') continue;
            $hash = url_hash($u);
            $hashToUrl[$hash] = $u;
        }
    }

    foreach ($repliesByParent as $items) {
        foreach ($items as $c) {
            $txt = (string)($c['body'] ?? '');
            $u = extract_first_url($txt);
            $u = normalize_url($u);
            if ($u === '') continue;
            $hash = url_hash($u);
            $hashToUrl[$hash] = $u;
        }
    }

    if ($hashToUrl) {
        $hashes = array_keys($hashToUrl);
        $in = implode(',', array_fill(0, count($hashes), '?'));
        $st = $pdo->prepare("SELECT url_hash, url, title, description, image, site_name FROM link_previews WHERE url_hash IN ($in)");
        $st->execute($hashes);
        $rows = $st->fetchAll() ?: [];
        foreach ($rows as $r) {
            $previewByHash[(string)$r['url_hash']] = $r;
        }
    }
}
?>

<style>
.vm-ava, .vm-ava-sm { width: 44px; height: 44px; border-radius: 999px; overflow: hidden; flex: 0 0 auto; }
.vm-ava-sm { width: 34px; height: 34px; }
.vm-ava img, .vm-ava-sm img { width: 100%; height: 100%; object-fit: cover; display:block; }
.vm-ava-ph { width:100%; height:100%; display:flex; align-items:center; justify-content:center; background:#e9ecef; color:#495057; font-weight:700; border-radius:999px; font-size:14px; }
.vm-menu { position:relative; }
.vm-menu-btn { list-style:none; cursor:pointer; user-select:none; border:0; background:transparent; padding:2px 6px; border-radius:8px; }
.vm-menu-btn::-webkit-details-marker { display:none; }
.vm-menu[open] .vm-menu-box { display:block; }
.vm-menu-box { display:none; position:absolute; right:0; top:24px; min-width:160px; background:#fff; border:1px solid rgba(0,0,0,.12); border-radius:12px; padding:6px; z-index:50; box-shadow:0 8px 24px rgba(0,0,0,.12); }
.vm-menu-item { display:block; width:100%; text-decoration:none; color:#212529; padding:8px 10px; border-radius:10px; }
.vm-menu-item:hover { background:#f1f3f5; }
.vm-menu-danger { color:#dc3545; background:transparent; border:0; text-align:left; }
.vm-repost-box { border:1px solid rgba(0,0,0,.08); border-radius:14px; padding:12px; background:#f8f9fa; }
.vm-repost-label { font-size: 12px; color:#6c757d; }

.vm-sponsored-badge{
  display:inline-flex;
  align-items:center;
  gap:6px;
  padding:4px 10px;
  border-radius:999px;
  font-size:12px;
  font-weight:700;
  background:rgba(255,193,7,.16);
  color:#8a5a00;
  border:1px solid rgba(255,193,7,.35);
}

.vm-desktop-ad{
  display:block;
}
.vm-side-ad-card{
  overflow:hidden;
}
.vm-side-ad-thumb{
  display:block;
  border-radius:14px;
  overflow:hidden;
  border:1px solid rgba(0,0,0,.06);
  background:#f8f9fa;
}
.vm-side-ad-thumb img{
  display:block;
  width:100%;
  height:auto;
}
.vm-side-ad-label{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:10px;
}
.vm-side-ad-title{
  font-size:13px;
  font-weight:600;
  color:#495057;
}
@media (max-width: 992px){
  .vm-desktop-ad{ display:none !important; }
}

.post-attachments video,
.comment-attachments video{
  display:block;
  width:100%;
  max-height:560px;
  height:auto;
  object-fit:contain;
  background:#000;
  border-radius:14px;
}
@media (max-width: 576px){
  .post-attachments video,
  .comment-attachments video{
    max-height:60vh;
  }
}

.lp-card-feed{ border:1px solid rgba(0,0,0,.12); border-radius:14px; overflow:hidden; background:#fff; }
.lp-card-feed .lp-card-inner{ display:flex; gap:12px; text-decoration:none; color:inherit; padding:12px; }
.lp-card-feed .lp-img{ width:110px; height:80px; border-radius:12px; overflow:hidden; flex:0 0 auto; background:#f1f3f5; }
.lp-card-feed .lp-img img{ width:100%; height:100%; object-fit:cover; display:block; }
.lp-card-feed .lp-meta{ min-width:0; }
.lp-card-feed .lp-title{ font-weight:700; line-height:1.2; margin-bottom:4px; }
.lp-card-feed .lp-desc{ color:#495057; font-size:13px; line-height:1.25; max-height:2.5em; overflow:hidden; }
.lp-card-feed .lp-site{ color:#6c757d; font-size:12px; margin-top:6px; }
</style>

<div class="feed-layout">
    <aside>
        <div class="sidebar-sticky">
            <?php require __DIR__ . '/blocks/profile.php'; ?>
            <?php require __DIR__ . '/blocks/reclama-left.php'; ?>
        </div>
    </aside>

    <section>
        <?php if (!$me): ?>
            <div class="card card-app p-4 mb-3">
                <h1 class="h4 mb-2">
                    <?= h($T(
                        'VMEREGI - перша українська соціальна мережа',
                        'VMEREGI - первая украинская социальная сеть',
                        'VMEREGI - the first Ukrainian social network'
                    )) ?>
                </h1>
                <div class="text-muted mb-3">
                    <?= h($T(
                        'Друзі, спілкування, групи та пости',
                        'Друзья, общение, группы и посты',
                        'Friends, messaging, groups and posts'
                    )) ?>
                </div>
                <div class="d-flex gap-2">
                    <a class="btn btn-primary" href="/register.php"><?= h(t('register')) ?></a>
                    <a class="btn btn-outline-secondary" href="/login.php"><?= h(t('login')) ?></a>
                </div>
            </div>
        <?php else: ?>
            <?php
            $postAction = '/functional/post/create.php';
            require __DIR__ . '/blocks/create_post.php';
            ?>
        <?php endif; ?>

        <div class="d-flex align-items-center justify-content-between mb-2">
            <div class="fw-semibold"><?= h(t('latest_posts')) ?></div>
        </div>

        <?php foreach ($posts as $p): ?>
            <?php require __DIR__ . '/blocks/feed_post_card.php'; ?>
        <?php endforeach; ?>
    </section>

    <aside>
        <div class="sidebar-sticky">
            <?php require __DIR__ . '/blocks/groups.php'; ?>

            <?php
            $gamesBlock = __DIR__ . '/blocks/games.php';
            if (is_file($gamesBlock)) require $gamesBlock;
            ?>

            <?php require __DIR__ . '/blocks/reclama-right.php'; ?>
        </div>
    </aside>
</div>

<div class="lb-overlay" id="lbOverlay" aria-hidden="true" style="display:none;">
    <div class="lb-backdrop" id="lbBackdrop"></div>
    <div class="lb-content" role="dialog" aria-modal="true" aria-label="Image viewer">
        <button class="lb-close" id="lbClose" type="button" aria-label="Close">&times;</button>
        <button class="lb-nav lb-prev" id="lbPrev" type="button" aria-label="Previous">&lsaquo;</button>
        <img class="lb-img" id="lbImg" alt="">
        <button class="lb-nav lb-next" id="lbNext" type="button" aria-label="Next">&rsaquo;</button>
        <div class="lb-counter" id="lbCounter"></div>
    </div>
</div>

<script>
(function(){
  if (window.__vmFeedReactInit) return;
  window.__vmFeedReactInit = true;

  const csrf = <?= json_encode(csrf_token(), JSON_UNESCAPED_UNICODE) ?>;

  const REACT_I18N = <?= json_encode([
    'likes' => $T('Лайків', 'Лайков', 'Likes'),
    'dislikes' => $T('Дизлайків', 'Дизлайков', 'Dislikes'),
  ], JSON_UNESCAPED_UNICODE) ?>;

  async function hydratePreview(el){
    const url = el.getAttribute('data-lp-url');
    if (!url) return;

    const img = el.querySelector('img');
    const hasImg = img && img.getAttribute('src');
    const hasDesc = el.querySelector('.lp-desc')?.textContent?.trim();
    const hasSite = el.querySelector('.lp-site')?.textContent?.trim();

    if (hasImg || hasDesc || hasSite) return;

    try{
      const res = await fetch('/link_preview.php?url=' + encodeURIComponent(url), {
        headers:{'Accept':'application/json'},
        credentials:'same-origin',
        cache:'no-store'
      });
      const data = await res.json();
      if (!data || !data.ok) return;

      const t = el.querySelector('.lp-title');
      if (t) t.textContent = (data.title || data.url || '').trim();

      if (data.description) {
        let d = el.querySelector('.lp-desc');
        if (!d) {
          d = document.createElement('div');
          d.className = 'lp-desc';
          el.querySelector('.lp-meta')?.appendChild(d);
        }
        d.textContent = data.description;
      }

      if (data.site_name) {
        let s = el.querySelector('.lp-site');
        if (!s) {
          s = document.createElement('div');
          s.className = 'lp-site';
          el.querySelector('.lp-meta')?.appendChild(s);
        }
        s.textContent = data.site_name;
      }

      if (data.image && img) {
        img.src = data.image;
        img.style.display = 'block';
      }
    }catch(e){}
  }

  async function parseJsonSafe(res){
    try {
      return await res.json();
    } catch (e) {
      return null;
    }
  }

  function setBusy(form, busy){
    form.dataset.busy = busy ? '1' : '0';
    const btns = form.querySelectorAll('button');
    btns.forEach((b) => { b.disabled = !!busy; });
  }

  async function submitPostReaction(form){
    if (!form || form.dataset.busy === '1') return;

    const postId = String(form.querySelector('input[name="post_id"]')?.value || '').trim();
    const reaction = String(form.querySelector('input[name="reaction"]')?.value || '').trim();
    if (!postId || !reaction) return;

    setBusy(form, true);

    try {
      const body = new URLSearchParams();
      body.set('csrf', csrf);
      body.set('post_id', postId);
      body.set('reaction', reaction);
      body.set('back', location.pathname + location.search + '#post-' + postId);

      const res = await fetch('/post_react.php', {
        method: 'POST',
        credentials: 'same-origin',
        cache: 'no-store',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
          'X-Requested-With': 'XMLHttpRequest',
          'Accept': 'application/json'
        },
        body
      });

      const data = await parseJsonSafe(res);
      if (!res.ok || !data || !data.ok) {
        location.reload();
        return;
      }

      const wrap = document.getElementById('post-' + postId);
      if (!wrap) return;

      const likeBtn = wrap.querySelector('button[data-react="like"]');
      const disBtn  = wrap.querySelector('button[data-react="dislike"]');
      const likesEl = document.getElementById('likes-' + postId);
      const dislikesEl = document.getElementById('dislikes-' + postId);

      if (likesEl) likesEl.textContent = String(data.likes ?? 0);
      if (dislikesEl) dislikesEl.textContent = String(data.dislikes ?? 0);

      if (likeBtn) {
        likeBtn.classList.remove('btn-outline-success','btn-success');
        likeBtn.classList.add(data.my === 'like' ? 'btn-success' : 'btn-outline-success');
        likeBtn.title = (REACT_I18N.likes || 'Likes') + ': ' + String(data.likes ?? 0);
      }

      if (disBtn) {
        disBtn.classList.remove('btn-outline-danger','btn-danger');
        disBtn.classList.add(data.my === 'dislike' ? 'btn-danger' : 'btn-outline-danger');

        disBtn.title = (REACT_I18N.dislikes || 'Dislikes') + ': ' + String(data.dislikes ?? 0);
      }

      window.setTimeout(() => {
        window.dispatchEvent(new CustomEvent('vmcoins:refresh'));
      }, 120);
    } catch (err) {
      location.reload();
    } finally {
      setBusy(form, false);
    }
  }

  async function submitCommentReaction(form){
    if (!form || form.dataset.busy === '1') return;

    const commentId = String(form.querySelector('input[name="comment_id"]')?.value || '').trim();
    const reaction = String(form.querySelector('input[name="reaction"]')?.value || '').trim();
    if (!commentId || !reaction) return;

    setBusy(form, true);

    try {
      const body = new URLSearchParams();
      body.set('csrf', csrf);
      body.set('comment_id', commentId);
      body.set('reaction', reaction);
      body.set('back', location.pathname + location.search + '#comment-' + commentId);

      const res = await fetch('/comment_react.php', {
        method: 'POST',
        credentials: 'same-origin',
        cache: 'no-store',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
          'X-Requested-With': 'XMLHttpRequest',
          'Accept': 'application/json'
        },
        body
      });

      const data = await parseJsonSafe(res);
      if (!res.ok || !data || !data.ok) {
        location.reload();
        return;
      }

      const likesEl = document.getElementById('clikes-' + commentId);
      const dislikesEl = document.getElementById('cdislikes-' + commentId);

      if (likesEl) likesEl.textContent = String(data.likes ?? 0);
      if (dislikesEl) dislikesEl.textContent = String(data.dislikes ?? 0);

      const root = document.getElementById('comment-' + commentId);
      if (root) {
        const likeBtn = root.querySelector('button[data-cmtreact="like"]');
        const disBtn  = root.querySelector('button[data-cmtreact="dislike"]');

        if (likeBtn) {
          likeBtn.classList.remove('btn-outline-success','btn-success');
          likeBtn.classList.add(data.my === 'like' ? 'btn-success' : 'btn-outline-success');
          likeBtn.title = (REACT_I18N.likes || 'Likes') + ': ' + String(data.likes ?? 0);
        }

        if (disBtn) {
          disBtn.classList.remove('btn-outline-danger','btn-danger');
          disBtn.classList.add(data.my === 'dislike' ? 'btn-danger' : 'btn-outline-danger');
          disBtn.title = (REACT_I18N.dislikes || 'Dislikes') + ': ' + String(data.dislikes ?? 0);
        }
      }

      window.setTimeout(() => {
        window.dispatchEvent(new CustomEvent('vmcoins:refresh'));
      }, 120);
    } catch (err) {
      location.reload();
    } finally {
      setBusy(form, false);
    }
  }

  const previewEls = Array.from(document.querySelectorAll('.lp-card-feed[data-lp-url]'));
  previewEls.forEach(hydratePreview);

  document.addEventListener('submit', (e) => {
    const form = e.target;
    if (!(form instanceof HTMLFormElement)) return;

    if (form.matches('form.js-post-react-form')) {
      e.preventDefault();
      e.stopPropagation();
      if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();
      submitPostReaction(form);
      return;
    }

    if (form.matches('form.js-comment-react-form')) {
      e.preventDefault();
      e.stopPropagation();
      if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();
      submitCommentReaction(form);
      return;
    }
  }, true);

  document.addEventListener('click', (e) => {
    const openBtn = e.target.closest('[data-reply-toggle]');
    if (openBtn) {
      const id = openBtn.getAttribute('data-reply-toggle');
      const box = document.getElementById('reply-form-' + id);
      if (box) box.classList.toggle('d-none');
      return;
    }

    const cancelBtn = e.target.closest('[data-reply-cancel]');
    if (cancelBtn) {
      const id = cancelBtn.getAttribute('data-reply-cancel');
      const box = document.getElementById('reply-form-' + id);
      if (box) box.classList.add('d-none');
      return;
    }
  });

  if (window.VMEmojiInit) window.VMEmojiInit();
})();
</script>

<?php
require __DIR__ . '/includes/emoji_picker.php';
require __DIR__ . '/includes/lightbox.php';
require __DIR__ . '/includes/footer.php';