using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Infrastructure.App.Persistence;

namespace TakeoutSaaS.Infrastructure.App.Repositories;

/// <summary>
/// EF 配额包仓储实现。
/// </summary>
public sealed class EfQuotaPackageRepository(TakeoutAppDbContext context) : IQuotaPackageRepository
{
    #region 配额包定义

    /// <inheritdoc />
    public Task<QuotaPackage?> FindByIdAsync(long id, CancellationToken cancellationToken = default)
    {
        return context.QuotaPackages
            .FirstOrDefaultAsync(x => x.Id == id && x.DeletedAt == null, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<(IReadOnlyList<QuotaPackage> Items, int Total)> SearchPagedAsync(
        TenantQuotaType? quotaType,
        bool? isActive,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        var query = context.QuotaPackages.AsNoTracking()
            .Where(x => x.DeletedAt == null);

        if (quotaType.HasValue)
        {
            query = query.Where(x => x.QuotaType == quotaType.Value);
        }

        if (isActive.HasValue)
        {
            query = query.Where(x => x.IsActive == isActive.Value);
        }

        var total = await query.CountAsync(cancellationToken);

        var items = await query
            .OrderBy(x => x.SortOrder)
            .ThenBy(x => x.CreatedAt)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync(cancellationToken);

        return (items, total);
    }

    /// <inheritdoc />
    public Task AddAsync(QuotaPackage quotaPackage, CancellationToken cancellationToken = default)
    {
        return context.QuotaPackages.AddAsync(quotaPackage, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task UpdateAsync(QuotaPackage quotaPackage, CancellationToken cancellationToken = default)
    {
        context.QuotaPackages.Update(quotaPackage);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task<bool> SoftDeleteAsync(long id, CancellationToken cancellationToken = default)
    {
        var quotaPackage = await context.QuotaPackages
            .FirstOrDefaultAsync(x => x.Id == id && x.DeletedAt == null, cancellationToken);

        if (quotaPackage == null)
        {
            return false;
        }

        quotaPackage.DeletedAt = DateTime.UtcNow;
        return true;
    }

    #endregion

    #region 配额包购买记录

    /// <inheritdoc />
    public async Task<(IReadOnlyList<(TenantQuotaPackagePurchase Purchase, QuotaPackage Package)> Items, int Total)> GetPurchasesPagedAsync(
        long tenantId,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        var query = context.TenantQuotaPackagePurchases
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.DeletedAt == null);

        var total = await query.CountAsync(cancellationToken);

        var items = await query
            .OrderByDescending(x => x.PurchasedAt)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Join(context.QuotaPackages.AsNoTracking(),
                purchase => purchase.QuotaPackageId,
                package => package.Id,
                (purchase, package) => new { Purchase = purchase, Package = package })
            .ToListAsync(cancellationToken);

        return (items.Select(x => (x.Purchase, x.Package)).ToList(), total);
    }

    /// <inheritdoc />
    public Task AddPurchaseAsync(TenantQuotaPackagePurchase purchase, CancellationToken cancellationToken = default)
    {
        return context.TenantQuotaPackagePurchases.AddAsync(purchase, cancellationToken).AsTask();
    }

    #endregion

    #region 配额使用情况

    /// <inheritdoc />
    public async Task<IReadOnlyList<TenantQuotaUsage>> GetUsageByTenantAsync(
        long tenantId,
        TenantQuotaType? quotaType,
        CancellationToken cancellationToken = default)
    {
        var query = context.TenantQuotaUsages
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId);

        if (quotaType.HasValue)
        {
            query = query.Where(x => x.QuotaType == quotaType.Value);
        }

        return await query.ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task<TenantQuotaUsage?> FindUsageAsync(
        long tenantId,
        TenantQuotaType quotaType,
        CancellationToken cancellationToken = default)
    {
        return context.TenantQuotaUsages
            .FirstOrDefaultAsync(x => x.TenantId == tenantId && x.QuotaType == quotaType, cancellationToken);
    }

    /// <inheritdoc />
    public Task UpdateUsageAsync(TenantQuotaUsage usage, CancellationToken cancellationToken = default)
    {
        context.TenantQuotaUsages.Update(usage);
        return Task.CompletedTask;
    }

    #endregion

    /// <inheritdoc />
    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        return context.SaveChangesAsync(cancellationToken);
    }
}
