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

namespace TakeoutSaaS.Infrastructure.App.Repositories;

/// <summary>
/// 新客有礼仓储 EF Core 实现。
/// </summary>
public sealed class EfNewCustomerGiftRepository(TakeoutAppDbContext context) : INewCustomerGiftRepository
{
    /// <inheritdoc />
    public Task<NewCustomerGiftSetting?> FindSettingByStoreIdAsync(
        long tenantId,
        long storeId,
        CancellationToken cancellationToken = default)
    {
        return context.NewCustomerGiftSettings
            .Where(item => item.TenantId == tenantId && item.StoreId == storeId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task AddSettingAsync(NewCustomerGiftSetting entity, CancellationToken cancellationToken = default)
    {
        return context.NewCustomerGiftSettings.AddAsync(entity, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task UpdateSettingAsync(NewCustomerGiftSetting entity, CancellationToken cancellationToken = default)
    {
        context.NewCustomerGiftSettings.Update(entity);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<NewCustomerCouponRule>> GetCouponRulesByStoreIdAsync(
        long tenantId,
        long storeId,
        CancellationToken cancellationToken = default)
    {
        return await context.NewCustomerCouponRules
            .AsNoTracking()
            .Where(item => item.TenantId == tenantId && item.StoreId == storeId)
            .OrderBy(item => item.Scene)
            .ThenBy(item => item.SortOrder)
            .ThenBy(item => item.Id)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task ReplaceCouponRulesAsync(
        long tenantId,
        long storeId,
        IReadOnlyCollection<NewCustomerCouponRule> entities,
        CancellationToken cancellationToken = default)
    {
        var existing = await context.NewCustomerCouponRules
            .Where(item => item.TenantId == tenantId && item.StoreId == storeId)
            .ToListAsync(cancellationToken);

        if (existing.Count > 0)
        {
            context.NewCustomerCouponRules.RemoveRange(existing);
        }

        if (entities.Count > 0)
        {
            await context.NewCustomerCouponRules.AddRangeAsync(entities, cancellationToken);
        }
    }

    /// <inheritdoc />
    public async Task<(IReadOnlyList<NewCustomerInviteRecord> Items, int TotalCount)> GetInviteRecordsAsync(
        long tenantId,
        long storeId,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        var normalizedPage = Math.Max(1, page);
        var normalizedPageSize = Math.Clamp(pageSize, 1, 200);

        var query = context.NewCustomerInviteRecords
            .AsNoTracking()
            .Where(item => item.TenantId == tenantId && item.StoreId == storeId);

        var totalCount = await query.CountAsync(cancellationToken);
        if (totalCount == 0)
        {
            return ([], 0);
        }

        var items = await query
            .OrderByDescending(item => item.InviteTime)
            .ThenByDescending(item => item.Id)
            .Skip((normalizedPage - 1) * normalizedPageSize)
            .Take(normalizedPageSize)
            .ToListAsync(cancellationToken);

        return (items, totalCount);
    }

    /// <inheritdoc />
    public Task AddInviteRecordAsync(NewCustomerInviteRecord entity, CancellationToken cancellationToken = default)
    {
        return context.NewCustomerInviteRecords.AddAsync(entity, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task<NewCustomerGrowthRecord?> FindGrowthRecordByCustomerKeyAsync(
        long tenantId,
        long storeId,
        string customerKey,
        CancellationToken cancellationToken = default)
    {
        return context.NewCustomerGrowthRecords
            .Where(item =>
                item.TenantId == tenantId &&
                item.StoreId == storeId &&
                item.CustomerKey == customerKey)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task AddGrowthRecordAsync(NewCustomerGrowthRecord entity, CancellationToken cancellationToken = default)
    {
        return context.NewCustomerGrowthRecords.AddAsync(entity, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task UpdateGrowthRecordAsync(NewCustomerGrowthRecord entity, CancellationToken cancellationToken = default)
    {
        context.NewCustomerGrowthRecords.Update(entity);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public Task<int> CountRegisteredCustomersAsync(
        long tenantId,
        long storeId,
        DateTime startAt,
        DateTime endAt,
        CancellationToken cancellationToken = default)
    {
        return context.NewCustomerGrowthRecords
            .AsNoTracking()
            .Where(item =>
                item.TenantId == tenantId &&
                item.StoreId == storeId &&
                item.RegisteredAt >= startAt &&
                item.RegisteredAt < endAt)
            .CountAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task<int> CountGiftClaimedCustomersAsync(
        long tenantId,
        long storeId,
        DateTime startAt,
        DateTime endAt,
        CancellationToken cancellationToken = default)
    {
        return context.NewCustomerGrowthRecords
            .AsNoTracking()
            .Where(item =>
                item.TenantId == tenantId &&
                item.StoreId == storeId &&
                item.RegisteredAt >= startAt &&
                item.RegisteredAt < endAt &&
                item.GiftClaimedAt != null)
            .CountAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task<int> CountFirstOrderedCustomersAsync(
        long tenantId,
        long storeId,
        DateTime startAt,
        DateTime endAt,
        CancellationToken cancellationToken = default)
    {
        return context.NewCustomerGrowthRecords
            .AsNoTracking()
            .Where(item =>
                item.TenantId == tenantId &&
                item.StoreId == storeId &&
                item.RegisteredAt >= startAt &&
                item.RegisteredAt < endAt &&
                item.FirstOrderAt != null)
            .CountAsync(cancellationToken);
    }

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