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

namespace TakeoutSaaS.Infrastructure.App.Repositories;

/// <summary>
/// 会员聚合 EF Core 仓储实现。
/// </summary>
public sealed class EfMemberRepository(TakeoutAppDbContext context) : IMemberRepository
{
    /// <inheritdoc />
    public async Task<IReadOnlyList<MemberProfile>> GetProfilesAsync(long tenantId, CancellationToken cancellationToken = default)
    {
        return await context.MemberProfiles
            .Where(x => x.TenantId == tenantId)
            .OrderByDescending(x => x.UpdatedAt ?? x.CreatedAt)
            .ThenByDescending(x => x.Id)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MemberProfile>> GetProfilesByMobilesAsync(
        long tenantId,
        IReadOnlyCollection<string> mobiles,
        CancellationToken cancellationToken = default)
    {
        if (mobiles.Count == 0)
        {
            return [];
        }

        return await context.MemberProfiles
            .Where(x => x.TenantId == tenantId && mobiles.Contains(x.Mobile))
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task<MemberProfile?> FindProfileByIdAsync(long tenantId, long memberId, CancellationToken cancellationToken = default)
    {
        return context.MemberProfiles
            .Where(x => x.TenantId == tenantId && x.Id == memberId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task AddProfilesAsync(IEnumerable<MemberProfile> profiles, CancellationToken cancellationToken = default)
    {
        var profileList = profiles?.ToList() ?? [];
        if (profileList.Count == 0)
        {
            return;
        }

        await context.MemberProfiles.AddRangeAsync(profileList, cancellationToken);
    }

    /// <inheritdoc />
    public Task UpdateProfileAsync(MemberProfile profile, CancellationToken cancellationToken = default)
    {
        context.MemberProfiles.Update(profile);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MemberTier>> GetTiersAsync(long tenantId, CancellationToken cancellationToken = default)
    {
        return await context.MemberTiers
            .Where(x => x.TenantId == tenantId)
            .OrderBy(x => x.SortOrder)
            .ThenBy(x => x.Id)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task<MemberTier?> FindTierByIdAsync(long tenantId, long tierId, CancellationToken cancellationToken = default)
    {
        return context.MemberTiers
            .Where(x => x.TenantId == tenantId && x.Id == tierId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task AddTierAsync(MemberTier tier, CancellationToken cancellationToken = default)
    {
        return context.MemberTiers.AddAsync(tier, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task UpdateTierAsync(MemberTier tier, CancellationToken cancellationToken = default)
    {
        context.MemberTiers.Update(tier);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public Task DeleteTierAsync(MemberTier tier, CancellationToken cancellationToken = default)
    {
        context.MemberTiers.Remove(tier);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public Task<MemberDaySetting?> GetMemberDaySettingAsync(long tenantId, CancellationToken cancellationToken = default)
    {
        return context.MemberDaySettings
            .Where(x => x.TenantId == tenantId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task AddMemberDaySettingAsync(MemberDaySetting setting, CancellationToken cancellationToken = default)
    {
        return context.MemberDaySettings.AddAsync(setting, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task UpdateMemberDaySettingAsync(MemberDaySetting setting, CancellationToken cancellationToken = default)
    {
        context.MemberDaySettings.Update(setting);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MemberProfileTag>> GetProfileTagsAsync(
        long tenantId,
        long memberProfileId,
        CancellationToken cancellationToken = default)
    {
        return await context.MemberProfileTags
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.MemberProfileId == memberProfileId)
            .OrderBy(x => x.TagName)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MemberProfileTag>> GetProfileTagsByMemberIdsAsync(
        long tenantId,
        IReadOnlyCollection<long> memberProfileIds,
        CancellationToken cancellationToken = default)
    {
        if (memberProfileIds.Count == 0)
        {
            return [];
        }

        return await context.MemberProfileTags
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && memberProfileIds.Contains(x.MemberProfileId))
            .OrderBy(x => x.MemberProfileId)
            .ThenBy(x => x.TagName)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task ReplaceProfileTagsAsync(
        long tenantId,
        long memberProfileId,
        IReadOnlyCollection<string> tags,
        CancellationToken cancellationToken = default)
    {
        var normalizedTags = (tags ?? Array.Empty<string>())
            .Select(x => (x ?? string.Empty).Trim())
            .Where(x => !string.IsNullOrWhiteSpace(x))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
            .ToList();

        var existing = await context.MemberProfileTags
            .Where(x => x.TenantId == tenantId && x.MemberProfileId == memberProfileId)
            .ToListAsync(cancellationToken);

        context.MemberProfileTags.RemoveRange(existing);

        if (normalizedTags.Count == 0)
        {
            return;
        }

        var entities = normalizedTags.Select(tag => new MemberProfileTag
        {
            MemberProfileId = memberProfileId,
            TagName = tag
        });

        await context.MemberProfileTags.AddRangeAsync(entities, cancellationToken);
    }

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