using Microsoft.EntityFrameworkCore;
using Tiku.Application.Commerce;
using Tiku.Domain.Commerce;

namespace Tiku.Infrastructure.Commerce;

internal sealed partial class CommerceAdminService
{
    public async Task<TenantCouponList> GetCouponsAsync(
        CommerceAdminActor actor,
        CommerceAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var coupons = dbContext.Coupons.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId);
        var items = await coupons
            .OrderByDescending(item => item.CreatedAt)
            .Take(Math.Clamp(query.Limit ?? 50, 1, 200))
            .ToArrayAsync(cancellationToken);
        return new TenantCouponList(items);
    }

    public async Task<Coupon> UpsertCouponAsync(
        CommerceAdminActor actor,
        UpsertCouponCommand command,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var coupon = command.Id.HasValue
            ? await dbContext.Coupons.SingleOrDefaultAsync(
                item => item.TenantId == actor.TenantId && item.Id == command.Id.Value,
                cancellationToken)
            : await dbContext.Coupons.SingleOrDefaultAsync(
                item => item.TenantId == actor.TenantId && item.Code == command.Code.Trim(),
                cancellationToken);
        if (coupon is null)
        {
            coupon = new Coupon { TenantId = actor.TenantId };
            dbContext.Coupons.Add(coupon);
        }

        coupon.Code = command.Code.Trim();
        coupon.PlanId = command.PlanId;
        coupon.DiscountType = command.DiscountType;
        coupon.DiscountValue = command.DiscountValue;
        coupon.ValidFrom = command.ValidFrom;
        coupon.ValidTo = command.ValidTo;
        coupon.MaxUses = command.MaxUses;
        coupon.Source = command.Source?.Trim();
        coupon.Remark = command.Remark;

        await dbContext.SaveChangesAsync(cancellationToken);
        return coupon;
    }

    public async Task<TenantCouponRedemptionList> GetCouponRedemptionsAsync(
        CommerceAdminActor actor,
        CommerceAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var redemptions = dbContext.CouponRedemptions.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId);
        if (!string.IsNullOrWhiteSpace(query.Status))
            redemptions = redemptions.Where(item => item.Status == ParseCouponRedemptionStatus(query.Status));

        var items = await redemptions
            .OrderByDescending(item => item.CreatedAt)
            .Take(Math.Clamp(query.Limit ?? 50, 1, 200))
            .ToArrayAsync(cancellationToken);
        return new TenantCouponRedemptionList(items);
    }

    public async Task<TenantCouponReport> GetCouponReportAsync(
        CommerceAdminActor actor,
        CommerceAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var couponCount =
            await dbContext.Coupons.CountAsync(item => item.TenantId == actor.TenantId, cancellationToken);
        var redemptions = dbContext.CouponRedemptions.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId);
        var claimedCount = await redemptions.CountAsync(cancellationToken);
        var usedCount =
            await redemptions.CountAsync(item => item.Status == CouponRedemptionStatus.Used, cancellationToken);
        var discountApplied = await redemptions
            .Where(item => item.Status == CouponRedemptionStatus.Used)
            .SumAsync(item => item.DiscountAppliedCents, cancellationToken) ?? 0;
        return new TenantCouponReport(couponCount, claimedCount, usedCount, discountApplied);
    }
}