BCards/src/BCards.Web/Services/UserPageService.cs
2025-07-14 23:21:25 -03:00

250 lines
7.7 KiB
C#

using BCards.Web.Models;
using BCards.Web.Repositories;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Text;
using BCards.Web.Utils;
namespace BCards.Web.Services;
public class UserPageService : IUserPageService
{
private readonly IUserPageRepository _userPageRepository;
private readonly IUserRepository _userRepository;
private readonly ISubscriptionRepository _subscriptionRepository;
public UserPageService(
IUserPageRepository userPageRepository,
IUserRepository userRepository,
ISubscriptionRepository subscriptionRepository)
{
_userPageRepository = userPageRepository;
_userRepository = userRepository;
_subscriptionRepository = subscriptionRepository;
}
public async Task<UserPage?> GetPageAsync(string category, string slug)
{
return await _userPageRepository.GetBySlugAsync(category, slug);
}
public async Task<UserPage?> GetUserPageAsync(string userId)
{
return await _userPageRepository.GetByUserIdAsync(userId);
}
public async Task<UserPage?> GetPageByIdAsync(string id)
{
return await _userPageRepository.GetByIdAsync(id);
}
public async Task<List<UserPage>> GetUserPagesAsync(string userId)
{
return await _userPageRepository.GetByUserIdAllAsync(userId);
}
public async Task<List<UserPage>> GetActivePagesAsync()
{
return await _userPageRepository.GetActivePagesAsync();
}
public async Task<UserPage> CreatePageAsync(UserPage userPage)
{
userPage.Slug = await GenerateSlugAsync(userPage.Category, userPage.DisplayName);
return await _userPageRepository.CreateAsync(userPage);
}
public async Task<UserPage> UpdatePageAsync(UserPage userPage)
{
return await _userPageRepository.UpdateAsync(userPage);
}
public async Task DeletePageAsync(string id)
{
await _userPageRepository.DeleteAsync(id);
}
public async Task<bool> ValidateSlugAsync(string category, string slug, string? excludeId = null)
{
if (string.IsNullOrWhiteSpace(slug) || string.IsNullOrWhiteSpace(category))
return false;
if (!IsValidSlugFormat(slug))
return false;
return !await _userPageRepository.SlugExistsAsync(category, slug, excludeId);
}
public async Task<string> GenerateSlugAsync(string category, string name)
{
var slug = SlugHelper.CreateSlug(GenerateSlug(name));
var originalSlug = slug;
var counter = 1;
while (await _userPageRepository.SlugExistsAsync(category, slug))
{
slug = $"{originalSlug}-{counter}";
counter++;
}
return slug;
}
public async Task<bool> CanCreateLinksAsync(string userId, int newLinksCount = 1)
{
var userPage = await _userPageRepository.GetByUserIdAsync(userId);
if (userPage == null) return true; // New page
var subscription = await _subscriptionRepository.GetByUserIdAsync(userId);
var maxLinks = subscription?.MaxLinks ?? 5; // Default free plan
if (maxLinks == -1) return true; // Unlimited
var currentLinksCount = userPage.Links?.Count(l => l.IsActive) ?? 0;
return (currentLinksCount + newLinksCount) <= maxLinks;
}
public async Task RecordPageViewAsync(string pageId, string? referrer = null, string? userAgent = null)
{
var page = await _userPageRepository.GetByIdAsync(pageId);
if (page?.PlanLimitations.AllowAnalytics != true) return;
var analytics = page.Analytics;
analytics.TotalViews++;
analytics.LastViewedAt = DateTime.UtcNow;
// Monthly stats
var monthKey = DateTime.UtcNow.ToString("yyyy-MM");
if (analytics.MonthlyViews.ContainsKey(monthKey))
analytics.MonthlyViews[monthKey]++;
else
analytics.MonthlyViews[monthKey] = 1;
// Referrer stats
if (!string.IsNullOrEmpty(referrer))
{
var domain = ExtractDomain(referrer);
if (!string.IsNullOrEmpty(domain))
{
if (analytics.TopReferrers.ContainsKey(domain))
analytics.TopReferrers[domain]++;
else
analytics.TopReferrers[domain] = 1;
}
}
// Device stats (simplified)
if (!string.IsNullOrEmpty(userAgent))
{
var deviceType = GetDeviceType(userAgent);
if (analytics.DeviceStats.ContainsKey(deviceType))
analytics.DeviceStats[deviceType]++;
else
analytics.DeviceStats[deviceType] = 1;
}
await _userPageRepository.UpdateAnalyticsAsync(pageId, analytics);
}
public async Task RecordLinkClickAsync(string pageId, int linkIndex)
{
var page = await _userPageRepository.GetByIdAsync(pageId);
if (page?.PlanLimitations.AllowAnalytics != true) return;
if (linkIndex >= 0 && linkIndex < page.Links.Count)
{
page.Links[linkIndex].Clicks++;
}
var analytics = page.Analytics;
analytics.TotalClicks++;
// Monthly clicks
var monthKey = DateTime.UtcNow.ToString("yyyy-MM");
if (analytics.MonthlyClicks.ContainsKey(monthKey))
analytics.MonthlyClicks[monthKey]++;
else
analytics.MonthlyClicks[monthKey] = 1;
await _userPageRepository.UpdateAsync(page);
}
public async Task<List<UserPage>> GetRecentPagesAsync(int limit = 10)
{
return await _userPageRepository.GetRecentPagesAsync(limit);
}
public async Task<List<UserPage>> GetPagesByCategoryAsync(string category, int limit = 20)
{
return await _userPageRepository.GetByCategoryAsync(category, limit);
}
private static string GenerateSlug(string text)
{
if (string.IsNullOrWhiteSpace(text))
return string.Empty;
// Remove acentos
text = RemoveDiacritics(text);
// Converter para minúsculas
text = text.ToLowerInvariant();
// Substituir espaços e caracteres especiais por hífens
text = Regex.Replace(text, @"[^a-z0-9\s-]", "");
text = Regex.Replace(text, @"[\s-]+", "-");
// Remover hífens do início e fim
text = text.Trim('-');
return text;
}
private static string RemoveDiacritics(string text)
{
var normalizedString = text.Normalize(NormalizationForm.FormD);
var stringBuilder = new StringBuilder();
foreach (var c in normalizedString)
{
var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
if (unicodeCategory != UnicodeCategory.NonSpacingMark)
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}
private static bool IsValidSlugFormat(string slug)
{
return Regex.IsMatch(slug, @"^[a-z0-9-]+$") && !slug.StartsWith('-') && !slug.EndsWith('-');
}
private static string ExtractDomain(string url)
{
try
{
var uri = new Uri(url);
return uri.Host;
}
catch
{
return string.Empty;
}
}
private static string GetDeviceType(string userAgent)
{
userAgent = userAgent.ToLowerInvariant();
if (userAgent.Contains("mobile") || userAgent.Contains("android") || userAgent.Contains("iphone"))
return "mobile";
if (userAgent.Contains("tablet") || userAgent.Contains("ipad"))
return "tablet";
return "desktop";
}
}