using FlatRender.IdentitySvc.Application.Services.Interfaces; using FlatRender.IdentitySvc.Domain.Entities; using FlatRender.IdentitySvc.Domain.Enums; using FlatRender.IdentitySvc.Infrastructure.Data; using FlatRender.IdentitySvc.Models.Requests; using FlatRender.IdentitySvc.Models.Responses; using Microsoft.EntityFrameworkCore; namespace FlatRender.IdentitySvc.Application.Services; public class DiscountService(IdentityDbContext db) : IDiscountService { public async Task ValidateAsync(Guid tenantId, string code, Guid? planId) { var discount = await db.Discounts.FirstOrDefaultAsync(d => d.TenantId == tenantId && d.Code == code && d.IsActive && (d.StartsAt == null || d.StartsAt <= DateTime.UtcNow) && (d.ExpiresAt == null || d.ExpiresAt >= DateTime.UtcNow) && (d.MaxUseCount == null || d.UsedCount < d.MaxUseCount)); if (discount == null) return new DiscountValidateResponse(false, 0, "Unknown", 0); if (planId.HasValue && discount.AppliesToPlanIds != null && discount.AppliesToPlanIds.Length > 0) { if (!discount.AppliesToPlanIds.Contains(planId.Value)) return new DiscountValidateResponse(false, 0, discount.Kind.ToString(), discount.Value); } long discountMinor = 0; if (planId.HasValue) { var plan = await db.Plans.FindAsync(planId.Value); if (plan != null) { discountMinor = discount.Kind == DiscountKind.Percentage ? (long)(plan.PriceMinor * (double)discount.Value / 100) : (long)discount.Value; } } return new DiscountValidateResponse(true, discountMinor, discount.Kind.ToString(), discount.Value); } public async Task> ListAsync(Guid tenantId, int page, int pageSize) { var total = await db.Discounts.LongCountAsync(d => d.TenantId == tenantId); var discounts = await db.Discounts .Where(d => d.TenantId == tenantId) .OrderByDescending(d => d.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); return new PagedResponse( discounts.Select(MapResponse).ToList(), new PaginationMeta(page, pageSize, total, total > (long)page * pageSize) ); } public async Task CreateAsync(Guid tenantId, CreateDiscountRequest request) { var exists = await db.Discounts.AnyAsync(d => d.TenantId == tenantId && d.Code == request.Code); if (exists) throw new InvalidOperationException("Discount code already exists"); if (!Enum.TryParse(request.Kind, true, out var kind)) throw new ArgumentException("Invalid discount kind"); var discount = new Discount { TenantId = tenantId, Name = request.Name, Code = request.Code.ToUpper(), Kind = kind, Value = request.Value, OwnerUserId = request.OwnerUserId, OwnerProfitPercentage = request.OwnerProfitPercentage, MaxUseCount = request.MaxUseCount, AppliesToPlanIds = request.AppliesToPlanIds, StartsAt = request.StartsAt, ExpiresAt = request.ExpiresAt, }; db.Discounts.Add(discount); await db.SaveChangesAsync(); return MapResponse(discount); } private static DiscountResponse MapResponse(Discount d) => new( d.Id, d.Name, d.Code, d.Kind.ToString(), d.Value, d.UsedCount, d.MaxUseCount, d.IsActive, d.ExpiresAt, d.CreatedAt ); }