using Application.Abstractions.Cache;
using Application.Abstractions.Chat;
using Application.Abstractions.Data;
using Application.Helpers;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Infrastructure.Chat;
///
/// 채팅 보상 훅 실구현 (d0 §2.2 / d3 M2). SendMessage 성공 시 ChatExpConfig.ChatXpPerMessage 를
/// RewardService 로 지급(ActionType=Chat, 일일 캡 적용, XP 전용)한다.
/// Singleton 등록(허브 계약 유지)이므로 scoped DbContext/Cache 는 스코프를 직접 생성해 사용한다.
/// 예외는 밖으로 던지지 않는다 — 채팅 브로드캐스트에 영향 금지.
///
internal sealed class RewardChatHook(IServiceScopeFactory scopeFactory, ILogger logger) : IChatRewardHook
{
public async Task OnMessageSentAsync(int memberID, string roomKey, string content, CancellationToken ct = default)
{
if (memberID <= 0)
{
return;
}
try
{
await using var scope = scopeFactory.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService();
var cache = scope.ServiceProvider.GetRequiredService();
// ChatExpConfig 는 Config.Get.Response 에 노출되지 않으므로 수치만 DB 에서 직접 조회.
var chatExp = await db.Config.AsNoTracking().OrderByDescending(c => c.ID).Select(c => c.ChatExp.ChatXpPerMessage).FirstOrDefaultAsync(ct);
if (chatExp <= 0)
{
return;
}
await RewardService.GrantForChatAsync(db, cache, memberID, chatExp, DateTime.UtcNow, ct);
await db.SaveChangesAsync(ct);
}
catch (Exception ex)
{
// 채팅 흐름을 막지 않는다 — 지급 실패만 로깅.
logger.LogWarning(ex, "채팅 보상 지급 실패 (memberID={MemberID}, roomKey={RoomKey})", memberID, roomKey);
}
}
}