using LiteCharms.Features.Extensions; using LiteCharms.Features.Models; using LiteCharms.Features.Shop.Leads.Models; using LiteCharms.Features.Shop.Postgres; using static LiteCharms.Features.Extensions.Hash; namespace LiteCharms.Features.Shop.Leads; public class LeadService(IDbContextFactory contextFactory) { public async ValueTask> CreateLeadAsync(CreateLead request, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var newLead = context.Leads.Add(new Entities.Lead { WebClickId = request.WebClickId, AppClickId = request.AppClickId, Source = request.Source, ClickId = request.ClickId, AdGroupId = request.AdGroupId, AdName = request.AdName, CampaignId = request.CampaignId, ClickLocation = request.ClickLocation, CustomerId = request.CustomerId, FeedItemId = request.FeedItemId, Status = LeadStatus.New, TargetId = request.TargetId, AttributionHash = GenerateSha256HashString.Invoke($"{request.ClickId}{request.AppClickId}{request.WebClickId}") }); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok(newLead.Entity.Id) : Result.Fail(new Error($"Failed to create lead -> Google ClickId: {request.ClickId}, App ClickId: {request.AppClickId}, Web ClickId: {request.WebClickId}")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetCustomerLeadsAsync(Guid customerId, DateRange range, CancellationToken cancellationToken = default) { try { var fromDate = range.From.ToDateTime(TimeOnly.MinValue); var toDate = range.To.ToDateTime(TimeOnly.MaxValue); using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var leads = await context.Leads.AsNoTracking() .OrderByDescending(o => o.CreatedAt) .Where(lead => lead.CustomerId == customerId) .Where(lead => lead.CreatedAt.Date >= fromDate && lead.CreatedAt.Date <= toDate) .ToArrayAsync(cancellationToken); return leads?.Length > 0 ? Result.Ok(leads.Select(l => l.ToModel()).ToArray()) : Result.Fail(new Error($"No customer {customerId} leads found for the specified date range {range.From} to {range.To}.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetLeadsAsync(DateRange range, CancellationToken cancellationToken = default) { try { var fromDate = range.From.ToDateTime(TimeOnly.MinValue); var toDate = range.To.ToDateTime(TimeOnly.MaxValue); using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var leads = await context.Leads.AsNoTracking() .OrderByDescending(o => o.CreatedAt) .Where(l => l.CreatedAt.Date >= fromDate && l.CreatedAt.Date <= toDate) .Take(range.MaxRecords) .ToArrayAsync(cancellationToken); return leads?.Length > 0 ? Result.Ok(leads.Select(l => l.ToModel()).ToArray()) : Result.Fail(new Error($"No leads found for the specified date range {range.From} to {range.To}.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask UpdateLeadAsync(Guid leadId, LeadStatus status, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var lead = await context.Leads.FirstOrDefaultAsync(l => l.Id == leadId, cancellationToken); if (lead is null) return Result.Fail(new Error($"Lead with ID {leadId} not found.")); lead.Status = status; lead.UpdatedAt = DateTime.UtcNow; return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail(new Error($"Failed to update the lead {leadId}.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } }