<?php
declare(strict_types=1);

/**
 * /comment_react.php
 *
 * Відповідає за:
 * - лайк / дизлайк коментаря
 * - AJAX / звичайний redirect
 * - повідомлення (notifications)
 * - безпечне нарахування / списання VM Coins автору коментаря
 */

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

$__vmCoinsLib = __DIR__ . '/functional/VMCoins/_lib.php';
if (is_file($__vmCoinsLib)) {
    require_once $__vmCoinsLib;
}
unset($__vmCoinsLib);

require_login();

if (!function_exists('json_out')) {
    function json_out(array $data, int $code = 200): void {
        http_response_code($code);
        header('Content-Type: application/json; charset=utf-8');
        header('Cache-Control: no-store');
        header('X-Content-Type-Options: nosniff');
        echo json_encode($data, JSON_UNESCAPED_UNICODE);
        exit;
    }
}

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

        if (preg_match('~^https?://~i', $raw)) {
            $p = parse_url($raw);
            $path = (string)($p['path'] ?? '/');
            $q = isset($p['query']) && $p['query'] !== '' ? ('?' . $p['query']) : '';
            $h = isset($p['fragment']) && $p['fragment'] !== '' ? ('#' . $p['fragment']) : '';
            $raw = $path . $q . $h;
        }

        if ($raw === '' || $raw[0] !== '/') return '/';
        if (str_starts_with($raw, '//')) return '/';

        return $raw;
    }
}

if (!function_exists('comment_react_db_has_table')) {
    function comment_react_db_has_table(PDO $pdo, string $table): bool {
        if (function_exists('db_has_table')) {
            try {
                return (bool)db_has_table($pdo, $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]);
            return (bool)$st->fetchColumn();
        } catch (Throwable $e) {
            return false;
        }
    }
}

if (!function_exists('comment_react_db_has_column')) {
    function comment_react_db_has_column(PDO $pdo, string $table, string $col): bool {
        if (function_exists('db_has_column')) {
            try {
                return (bool)db_has_column($pdo, $table, $col);
            } 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, $col]);
            return (bool)$st->fetchColumn();
        } catch (Throwable $e) {
            return false;
        }
    }
}

if (!function_exists('comment_react_notifications_ensure_schema')) {
    function comment_react_notifications_ensure_schema(PDO $pdo): bool {
        try {
            $pdo->exec("
                CREATE TABLE IF NOT EXISTS `notifications` (
                  `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
                  `user_id` INT(10) UNSIGNED NOT NULL,
                  `actor_user_id` INT(10) UNSIGNED DEFAULT NULL,
                  `type` VARCHAR(50) NOT NULL,
                  `entity_type` VARCHAR(50) DEFAULT NULL,
                  `entity_id` BIGINT(20) UNSIGNED DEFAULT NULL,
                  `post_id` BIGINT(20) UNSIGNED DEFAULT NULL,
                  `group_id` INT(10) UNSIGNED DEFAULT NULL,
                  `message` VARCHAR(500) DEFAULT NULL,
                  `is_read` TINYINT(1) NOT NULL DEFAULT 0,
                  `created_at` DATETIME NOT NULL,
                  `read_at` DATETIME DEFAULT NULL,
                  PRIMARY KEY (`id`),
                  KEY `idx_user_read_created` (`user_id`,`is_read`,`created_at`),
                  KEY `idx_type_entity` (`type`,`entity_type`,`entity_id`),
                  KEY `idx_actor_user` (`actor_user_id`),
                  KEY `idx_post_id` (`post_id`),
                  KEY `idx_group_id` (`group_id`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
            ");
            return true;
        } catch (Throwable $e) {
            error_log('comment_react.php notifications schema error: ' . $e->getMessage());
            return false;
        }
    }
}

if (!function_exists('comment_react_notification_message')) {
    function comment_react_notification_message(string $reaction): string {
        $lang = function_exists('lang') ? (string)lang() : (string)($_SESSION['lang'] ?? 'uk');
        if (!in_array($lang, ['uk', 'ru', 'en'], true)) $lang = 'uk';

        return match ($reaction) {
            'like' => match ($lang) {
                'ru' => 'поставил(а) лайк вашему комментарию',
                'en' => 'liked your comment',
                default => 'поставив(ла) лайк вашому коментарю',
            },
            'dislike' => match ($lang) {
                'ru' => 'поставил(а) дизлайк вашему комментарию',
                'en' => 'disliked your comment',
                default => 'поставив(ла) дизлайк вашому коментарю',
            },
            default => match ($lang) {
                'ru' => 'отреагировал(а) на ваш комментарий',
                'en' => 'reacted to your comment',
                default => 'відреагував(ла) на ваш коментар',
            },
        };
    }
}

if (!function_exists('comment_react_notification_delete')) {
    function comment_react_notification_delete(PDO $pdo, int $ownerUserId, int $actorUserId, int $commentId): void {
        if (!comment_react_db_has_table($pdo, 'notifications')) return;

        try {
            $st = $pdo->prepare("
                DELETE FROM notifications
                WHERE user_id = ?
                  AND actor_user_id = ?
                  AND entity_type = 'comment'
                  AND entity_id = ?
                  AND type IN ('comment_like', 'comment_dislike')
            ");
            $st->execute([$ownerUserId, $actorUserId, $commentId]);
        } catch (Throwable $e) {
            error_log('comment_react.php notification delete error: ' . $e->getMessage());
        }
    }
}

if (!function_exists('comment_react_notification_upsert')) {
    function comment_react_notification_upsert(
        PDO $pdo,
        int $ownerUserId,
        int $actorUserId,
        int $commentId,
        int $postId,
        int $groupId,
        string $reaction
    ): void {
        if ($ownerUserId <= 0 || $actorUserId <= 0 || $commentId <= 0) return;
        if ($ownerUserId === $actorUserId) return;

        if (!comment_react_notifications_ensure_schema($pdo)) return;
        if (!comment_react_db_has_table($pdo, 'notifications')) return;

        $type = $reaction === 'dislike' ? 'comment_dislike' : 'comment_like';
        $message = comment_react_notification_message($reaction);

        try {
            $st = $pdo->prepare("
                SELECT id
                FROM notifications
                WHERE user_id = ?
                  AND actor_user_id = ?
                  AND entity_type = 'comment'
                  AND entity_id = ?
                  AND type IN ('comment_like', 'comment_dislike')
                ORDER BY id DESC
                LIMIT 1
            ");
            $st->execute([$ownerUserId, $actorUserId, $commentId]);
            $nid = (int)($st->fetchColumn() ?: 0);

            if ($nid > 0) {
                $up = $pdo->prepare("
                    UPDATE notifications
                    SET type = ?,
                        entity_type = 'comment',
                        entity_id = ?,
                        post_id = ?,
                        group_id = ?,
                        message = ?,
                        is_read = 0,
                        read_at = NULL,
                        created_at = NOW()
                    WHERE id = ?
                    LIMIT 1
                ");
                $up->execute([
                    $type,
                    $commentId,
                    ($postId > 0 ? $postId : null),
                    ($groupId > 0 ? $groupId : null),
                    $message,
                    $nid
                ]);
            } else {
                $ins = $pdo->prepare("
                    INSERT INTO notifications
                        (user_id, actor_user_id, type, entity_type, entity_id, post_id, group_id, message, is_read, created_at, read_at)
                    VALUES
                        (?, ?, ?, 'comment', ?, ?, ?, ?, 0, NOW(), NULL)
                ");
                $ins->execute([
                    $ownerUserId,
                    $actorUserId,
                    $type,
                    $commentId,
                    ($postId > 0 ? $postId : null),
                    ($groupId > 0 ? $groupId : null),
                    $message
                ]);
            }
        } catch (Throwable $e) {
            error_log('comment_react.php notification upsert error: ' . $e->getMessage());
        }
    }
}

if (!function_exists('comment_react_try_apply_vmcoins')) {
    function comment_react_try_apply_vmcoins(
        PDO $pdo,
        int $actorUserId,
        int $commentOwnerId,
        int $commentId,
        ?string $oldReaction,
        ?string $newReaction
    ): void {
        if ($actorUserId <= 0 || $commentOwnerId <= 0 || $commentId <= 0) {
            return;
        }

        if (!function_exists('vmc_apply_comment_reaction_transition')) {
            return;
        }

        try {
            $oldReaction = ($oldReaction !== null && $oldReaction !== '') ? $oldReaction : null;
            $newReaction = ($newReaction !== null && $newReaction !== '') ? $newReaction : null;

            $opKey = 'vmc:comment_reaction:' . $commentId . ':' . $actorUserId . ':' . ($oldReaction ?? 'none') . '->' . ($newReaction ?? 'none');

            $res = vmc_apply_comment_reaction_transition(
                $pdo,
                $actorUserId,
                $commentOwnerId,
                $commentId,
                $oldReaction,
                $newReaction,
                $opKey
            );

            if (!is_array($res)) {
                return;
            }

            $ok = (bool)($res['ok'] ?? false);
            if ($ok) {
                return;
            }

            $reason = (string)($res['reason'] ?? '');
            $silentReasons = [
                'disabled',
                'schema_missing',
                'zero_delta',
                'self_reaction_skip',
                'bad_input',
            ];

            if (!in_array($reason, $silentReasons, true)) {
                error_log('comment_react.php vmcoins error: ' . json_encode($res, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
            }
        } catch (Throwable $e) {
            error_log('comment_react.php vmcoins exception: ' . $e->getMessage());
        }
    }
}

$isAjax = (strtolower((string)($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '')) === 'xmlhttprequest')
    || str_contains(strtolower((string)($_SERVER['HTTP_ACCEPT'] ?? '')), 'application/json');

$back = safe_back_url((string)post_str('back'));
if ($back === '/' || $back === '') {
    $ref = (string)($_SERVER['HTTP_REFERER'] ?? '');
    $back = safe_back_url($ref);
}

if (!is_post()) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'method'], 405);
    http_response_code(405);
    echo "Method Not Allowed";
    exit;
}

if (!csrf_check(post_str('csrf'))) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'csrf'], 400);
    flash_set('err', t('bad_csrf'));
    redirect($back);
}

$commentId = (int)post_str('comment_id');
$reaction  = (string)post_str('reaction');

if ($commentId <= 0 || !in_array($reaction, ['like', 'dislike'], true)) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'bad'], 400);
    redirect($back);
}

/* ===== load comment + post context ===== */
$hasPostGroupId = comment_react_db_has_column($pdo, 'posts', 'group_id');

if ($hasPostGroupId) {
    $st = $pdo->prepare("
        SELECT c.id, c.post_id, c.user_id, p.group_id
        FROM comments c
        JOIN posts p ON p.id = c.post_id
        WHERE c.id = ?
        LIMIT 1
    ");
} else {
    $st = $pdo->prepare("
        SELECT c.id, c.post_id, c.user_id, 0 AS group_id
        FROM comments c
        WHERE c.id = ?
        LIMIT 1
    ");
}
$st->execute([$commentId]);
$commentRow = $st->fetch(PDO::FETCH_ASSOC);

if (!$commentRow) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'notfound'], 404);
    redirect($back);
}

$postId = (int)($commentRow['post_id'] ?? 0);
$commentOwnerId = (int)($commentRow['user_id'] ?? 0);
$groupId = $hasPostGroupId ? (int)($commentRow['group_id'] ?? 0) : 0;

if ($postId <= 0) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'notfound'], 404);
    redirect($back);
}

/* якщо back порожній — віддамо максимально корисний fallback */
if ($back === '/' || $back === '') {
    if ($groupId > 0) {
        $back = '/functional/group/view.php?id=' . $groupId . '#post-' . $postId;
    } else {
        $back = '/#comment-' . $commentId;
    }
}

/* ===== current reaction ===== */
$st = $pdo->prepare("SELECT reaction FROM comment_reactions WHERE comment_id=? AND user_id=? LIMIT 1");
$st->execute([$commentId, (int)$me['id']]);
$cur = $st->fetchColumn();
$cur = $cur ? (string)$cur : '';

$actorUserId = (int)($me['id'] ?? 0);

$pdo->beginTransaction();
try {
    if ($cur === '') {
        $st = $pdo->prepare("INSERT INTO comment_reactions (comment_id, user_id, reaction, created_at) VALUES (?,?,?,NOW())");
        $st->execute([$commentId, $actorUserId, $reaction]);
        $new = $reaction;
    } elseif ($cur === $reaction) {
        $st = $pdo->prepare("DELETE FROM comment_reactions WHERE comment_id=? AND user_id=? LIMIT 1");
        $st->execute([$commentId, $actorUserId]);
        $new = '';
    } else {
        $st = $pdo->prepare("UPDATE comment_reactions SET reaction=?, created_at=NOW() WHERE comment_id=? AND user_id=? LIMIT 1");
        $st->execute([$reaction, $commentId, $actorUserId]);
        $new = $reaction;
    }

    $pdo->commit();

    /* ===== VM Coins after successful commit ===== */
    comment_react_try_apply_vmcoins(
        $pdo,
        $actorUserId,
        $commentOwnerId,
        $commentId,
        ($cur !== '' ? $cur : null),
        ($new !== '' ? $new : null)
    );

    /* ===== notifications after successful commit ===== */
    try {
        if ($commentOwnerId > 0 && $actorUserId > 0 && $commentOwnerId !== $actorUserId) {
            if ($new === '') {
                comment_react_notification_delete($pdo, $commentOwnerId, $actorUserId, $commentId);
            } else {
                comment_react_notification_upsert(
                    $pdo,
                    $commentOwnerId,
                    $actorUserId,
                    $commentId,
                    $postId,
                    $groupId,
                    $new
                );
            }
        }
    } catch (Throwable $e) {
        error_log('comment_react.php notification phase error: ' . $e->getMessage());
    }

    /* ===== recount ===== */
    $st = $pdo->prepare("
        SELECT
          SUM(reaction='like') AS likes,
          SUM(reaction='dislike') AS dislikes
        FROM comment_reactions
        WHERE comment_id=?
    ");
    $st->execute([$commentId]);
    $row = $st->fetch(PDO::FETCH_ASSOC) ?: ['likes' => 0, 'dislikes' => 0];

    $payload = [
        'ok' => true,
        'comment_id' => $commentId,
        'post_id' => $postId,
        'group_id' => $groupId,
        'likes' => (int)($row['likes'] ?? 0),
        'dislikes' => (int)($row['dislikes'] ?? 0),
        'my' => $new,
    ];

    if ($isAjax) json_out($payload, 200);

    redirect($back);
} catch (Throwable $e) {
    if ($pdo->inTransaction()) $pdo->rollBack();

    error_log('comment_react.php error: ' . $e->getMessage());

    if ($isAjax) json_out(['ok' => false, 'error' => 'server'], 500);

    flash_set('err', t('error') ?: 'Error');
    redirect($back);
}