using Application.Abstractions.Data; using Application.Abstractions.Messaging; using SharedKernel.Results; using Domain.Entities.Donations; using Domain.Entities.Donations.ValueObject; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Crew.SaveWidgetConfig; internal sealed class Handler(IAppDbContext db) : ICommandHandler { public async Task Handle(Command r, CancellationToken ct) { var theme = (CrewWidgetTheme)r.Theme; var period = (RankPeriodType)r.Period; // CrewID 검증 — 채널 소유 Crew인지 var crewExists = await db.Crew.AnyAsync(c => c.ID == r.CrewID && c.ChannelID == r.ChannelID, ct); if (!crewExists) { return Result.Failure(Error.NotFound("Crew.NotFound", "선택한 크루를 찾을 수 없습니다.")); } // 기간 중복 검증 — 같은 Crew + 같은 Period 활성 설정 충돌 방지 if (r.IsActive) { if (period == RankPeriodType.Custom && r.StartAt.HasValue && r.EndAt.HasValue) { var overlapping = await db.CrewWidgetConfig .AnyAsync(c => c.ChannelID == r.ChannelID && c.CrewID == r.CrewID && (!r.ID.HasValue || c.ID != r.ID.Value) && c.IsActive && c.Period == RankPeriodType.Custom && c.StartAt < r.EndAt && c.EndAt > r.StartAt, ct); if (overlapping) { return Result.Failure(Error.Conflict("CrewWidget.PeriodOverlap", "기간이 겹치는 활성 설정이 있습니다.")); } } else { var existing = await db.CrewWidgetConfig .AnyAsync(c => c.ChannelID == r.ChannelID && c.CrewID == r.CrewID && (!r.ID.HasValue || c.ID != r.ID.Value) && c.IsActive && c.Period == period, ct); if (existing) { return Result.Failure(Error.Conflict("CrewWidget.SamePeriodExists", "같은 크루에 같은 기간의 활성 설정이 이미 있습니다.")); } } } if (r.ID.HasValue) { var config = await db.CrewWidgetConfig .FirstOrDefaultAsync(c => c.ID == r.ID.Value && c.ChannelID == r.ChannelID, ct); if (config is null) { return Result.Failure(Error.NotFound("CrewWidget.NotFound", "설정을 찾을 수 없습니다.")); } config.Update( r.CrewID, r.Title, theme, period, r.StartAt, r.EndAt, r.MaxDisplayCount, r.IsShowAmount, r.IsShowDonationCount, r.IsShowContributionRate, r.IsShowMemberIcon, r.IsActive, r.BgColor, r.TitleFontFamily, r.TitleFontSizePx, r.TitleFontColor, r.Rank1FontFamily, r.Rank1FontSizePx, r.Rank1FontColor, r.Rank2FontFamily, r.Rank2FontSizePx, r.Rank2FontColor, r.Rank3FontFamily, r.Rank3FontSizePx, r.Rank3FontColor, r.RowFontFamily, r.RowFontSizePx, r.RowFontColor ); } else { var config = CrewWidgetConfig.Create( r.ChannelID, r.MemberID, r.CrewID, r.Title, theme, period, r.StartAt, r.EndAt, r.MaxDisplayCount, r.IsShowAmount, r.IsShowDonationCount, r.IsShowContributionRate, r.IsShowMemberIcon, r.IsActive, r.BgColor, r.TitleFontFamily, r.TitleFontSizePx, r.TitleFontColor, r.Rank1FontFamily, r.Rank1FontSizePx, r.Rank1FontColor, r.Rank2FontFamily, r.Rank2FontSizePx, r.Rank2FontColor, r.Rank3FontFamily, r.Rank3FontSizePx, r.Rank3FontColor, r.RowFontFamily, r.RowFontSizePx, r.RowFontColor ); db.CrewWidgetConfig.Add(config); } await db.SaveChangesAsync(ct); return Result.Success(); } }