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 EfTenantAnnouncementRepository(TakeoutAppDbContext context) : ITenantAnnouncementRepository
{
    /// <inheritdoc />
    public async Task<IReadOnlyList<TenantAnnouncement>> SearchAsync(
        long tenantId,
        string? keyword,
        AnnouncementStatus? status,
        TenantAnnouncementType? type,
        bool? isActive,
        DateTime? effectiveFrom,
        DateTime? effectiveTo,
        DateTime? effectiveAt,
        bool orderByPriority = false,
        int? limit = null,
        CancellationToken cancellationToken = default)
    {
        var query = context.TenantAnnouncements.AsNoTracking()
            .Where(x => x.TenantId == tenantId);

        if (!string.IsNullOrWhiteSpace(keyword))
        {
            var normalized = keyword.Trim();
            query = query.Where(x =>
                EF.Functions.ILike(x.Title, $"%{normalized}%")
                || EF.Functions.ILike(x.Content, $"%{normalized}%"));
        }

        if (status.HasValue)
        {
            query = query.Where(x => x.Status == status.Value);
        }

        if (type.HasValue)
        {
            query = query.Where(x => x.AnnouncementType == type.Value);
        }

        if (isActive.HasValue)
        {
            query = isActive.Value
                ? query.Where(x => x.Status == AnnouncementStatus.Published)
                : query.Where(x => x.Status != AnnouncementStatus.Published);
        }

        if (effectiveFrom.HasValue)
        {
            query = query.Where(x => x.EffectiveFrom >= effectiveFrom.Value);
        }

        if (effectiveTo.HasValue)
        {
            query = query.Where(x => x.EffectiveTo == null || x.EffectiveTo <= effectiveTo.Value);
        }

        if (effectiveAt.HasValue)
        {
            var at = effectiveAt.Value;
            query = query.Where(x => x.EffectiveFrom <= at && (x.EffectiveTo == null || x.EffectiveTo >= at));
        }

        // 应用排序（如果启用）
        if (orderByPriority)
        {
            query = query.OrderByDescending(x => x.Priority).ThenByDescending(x => x.EffectiveFrom);
        }

        // 应用限制（如果指定）
        if (limit.HasValue && limit.Value > 0)
        {
            query = query.Take(limit.Value);
        }

        return await query.ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<TenantAnnouncement>> SearchUnreadAsync(
        long tenantId,
        long? userId,
        AnnouncementStatus? status,
        bool? isActive,
        DateTime? effectiveAt,
        CancellationToken cancellationToken = default)
    {
        var announcementQuery = context.TenantAnnouncements.AsNoTracking()
            .Where(x => x.TenantId == tenantId);

        if (status.HasValue)
        {
            announcementQuery = announcementQuery.Where(x => x.Status == status.Value);
        }

        if (isActive.HasValue)
        {
            announcementQuery = isActive.Value
                ? announcementQuery.Where(x => x.Status == AnnouncementStatus.Published)
                : announcementQuery.Where(x => x.Status != AnnouncementStatus.Published);
        }

        if (effectiveAt.HasValue)
        {
            var at = effectiveAt.Value;
            announcementQuery = announcementQuery.Where(x => x.EffectiveFrom <= at && (x.EffectiveTo == null || x.EffectiveTo >= at));
        }

        var readQuery = context.TenantAnnouncementReads.AsNoTracking()
            .Where(x => x.TenantId == tenantId);

        readQuery = userId.HasValue
            ? readQuery.Where(x => x.UserId == null || x.UserId == userId.Value)
            : readQuery.Where(x => x.UserId == null);

        var query = from announcement in announcementQuery
            join read in readQuery on announcement.Id equals read.AnnouncementId into readGroup
            where !readGroup.Any()
            select announcement;

        return await query.ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task<TenantAnnouncement?> FindByIdAsync(long tenantId, long announcementId, CancellationToken cancellationToken = default)
    {
        return context.TenantAnnouncements.AsNoTracking()
            .FirstOrDefaultAsync(x => x.TenantId == tenantId && x.Id == announcementId, cancellationToken);
    }

    /// <inheritdoc />
    public Task AddAsync(TenantAnnouncement announcement, CancellationToken cancellationToken = default)
    {
        return context.TenantAnnouncements.AddAsync(announcement, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task UpdateAsync(TenantAnnouncement announcement, CancellationToken cancellationToken = default)
    {
        context.TenantAnnouncements.Update(announcement);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task DeleteAsync(long tenantId, long announcementId, CancellationToken cancellationToken = default)
    {
        var entity = await context.TenantAnnouncements.FirstOrDefaultAsync(x => x.TenantId == tenantId && x.Id == announcementId, cancellationToken);
        if (entity != null)
        {
            context.TenantAnnouncements.Remove(entity);
        }
    }

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