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

namespace TakeoutSaaS.Infrastructure.App.Repositories;

/// <summary>
/// 订阅管理仓储实现。
/// </summary>
public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext, TakeoutLogsDbContext logsContext) : ISubscriptionRepository
{
    #region 订阅查询

    /// <inheritdoc />
    public async Task<TenantSubscription?> FindByIdAsync(
        long subscriptionId,
        CancellationToken cancellationToken = default)
    {
        return await dbContext.TenantSubscriptions
            .FirstOrDefaultAsync(s => s.Id == subscriptionId, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<TenantSubscription>> FindByIdsAsync(
        IEnumerable<long> subscriptionIds,
        CancellationToken cancellationToken = default)
    {
        var ids = subscriptionIds.ToList();
        return await dbContext.TenantSubscriptions
            .Where(s => ids.Contains(s.Id))
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<(IReadOnlyList<SubscriptionWithRelations> Items, int Total)> SearchPagedAsync(
        SubscriptionSearchFilter filter,
        CancellationToken cancellationToken = default)
    {
        // 1. 构建基础查询
        var query = dbContext.TenantSubscriptions
            .AsNoTracking()
            .Join(
                dbContext.Tenants,
                sub => sub.TenantId,
                tenant => tenant.Id,
                (sub, tenant) => new { Subscription = sub, Tenant = tenant }
            )
            .Join(
                dbContext.TenantPackages,
                combined => combined.Subscription.TenantPackageId,
                package => package.Id,
                (combined, package) => new { combined.Subscription, combined.Tenant, Package = package }
            )
            .GroupJoin(
                dbContext.TenantPackages,
                combined => combined.Subscription.ScheduledPackageId,
                scheduledPackage => scheduledPackage.Id,
                (combined, scheduledPackages) => new { combined.Subscription, combined.Tenant, combined.Package, ScheduledPackage = scheduledPackages.FirstOrDefault() }
            );

        // 2. 应用过滤条件
        if (filter.Status.HasValue)
        {
            query = query.Where(x => x.Subscription.Status == filter.Status.Value);
        }

        if (filter.TenantPackageId.HasValue)
        {
            query = query.Where(x => x.Subscription.TenantPackageId == filter.TenantPackageId.Value);
        }

        if (filter.TenantId.HasValue)
        {
            query = query.Where(x => x.Subscription.TenantId == filter.TenantId.Value);
        }

        if (!string.IsNullOrWhiteSpace(filter.TenantKeyword))
        {
            var keyword = filter.TenantKeyword.Trim().ToLower();
            query = query.Where(x => x.Tenant.Name.ToLower().Contains(keyword) || x.Tenant.Code.ToLower().Contains(keyword));
        }

        if (filter.ExpiringWithinDays.HasValue)
        {
            var expiryDate = DateTime.UtcNow.AddDays(filter.ExpiringWithinDays.Value);
            query = query.Where(x => x.Subscription.EffectiveTo <= expiryDate && x.Subscription.EffectiveTo >= DateTime.UtcNow);
        }

        if (filter.AutoRenew.HasValue)
        {
            query = query.Where(x => x.Subscription.AutoRenew == filter.AutoRenew.Value);
        }

        // 3. 获取总数
        var total = await query.CountAsync(cancellationToken);

        // 4. 排序和分页
        var items = await query
            .OrderByDescending(x => x.Subscription.CreatedAt)
            .Skip((filter.Page - 1) * filter.PageSize)
            .Take(filter.PageSize)
            .Select(x => new SubscriptionWithRelations
            {
                Subscription = x.Subscription,
                TenantName = x.Tenant.Name,
                TenantCode = x.Tenant.Code,
                PackageName = x.Package.Name,
                ScheduledPackageName = x.ScheduledPackage != null ? x.ScheduledPackage.Name : null
            })
            .ToListAsync(cancellationToken);

        return (items, total);
    }

    /// <inheritdoc />
    public async Task<SubscriptionDetailInfo?> GetDetailAsync(
        long subscriptionId,
        CancellationToken cancellationToken = default)
    {
        var result = await dbContext.TenantSubscriptions
            .AsNoTracking()
            .Where(s => s.Id == subscriptionId)
            .Select(s => new
            {
                Subscription = s,
                Tenant = dbContext.Tenants.FirstOrDefault(t => t.Id == s.TenantId),
                Package = dbContext.TenantPackages.FirstOrDefault(p => p.Id == s.TenantPackageId),
                ScheduledPackage = s.ScheduledPackageId.HasValue
                    ? dbContext.TenantPackages.FirstOrDefault(p => p.Id == s.ScheduledPackageId)
                    : null
            })
            .FirstOrDefaultAsync(cancellationToken);

        if (result == null)
        {
            return null;
        }

        return new SubscriptionDetailInfo
        {
            Subscription = result.Subscription,
            TenantName = result.Tenant?.Name ?? "",
            TenantCode = result.Tenant?.Code ?? "",
            Package = result.Package,
            ScheduledPackage = result.ScheduledPackage
        };
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<SubscriptionWithTenant>> FindByIdsWithTenantAsync(
        IEnumerable<long> subscriptionIds,
        CancellationToken cancellationToken = default)
    {
        var ids = subscriptionIds.ToList();

        return await dbContext.TenantSubscriptions
            .Where(s => ids.Contains(s.Id))
            .Join(
                dbContext.Tenants,
                sub => sub.TenantId,
                tenant => tenant.Id,
                (sub, tenant) => new SubscriptionWithTenant
                {
                    Subscription = sub,
                    Tenant = tenant
                }
            )
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<AutoRenewalCandidate>> FindAutoRenewalCandidatesAsync(
        DateTime now,
        DateTime renewalThreshold,
        CancellationToken cancellationToken = default)
    {
        // 1. 查询开启自动续费且即将到期的活跃订阅
        var query = dbContext.TenantSubscriptions
            .Where(s => s.Status == SubscriptionStatus.Active
                        && s.AutoRenew
                        && s.EffectiveTo <= renewalThreshold
                        && s.EffectiveTo > now)
            .Join(
                dbContext.TenantPackages,
                subscription => subscription.TenantPackageId,
                package => package.Id,
                (subscription, package) => new AutoRenewalCandidate
                {
                    Subscription = subscription,
                    Package = package
                });

        // 2. 返回候选列表
        return await query.ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<RenewalReminderCandidate>> FindRenewalReminderCandidatesAsync(
        DateTime startOfDay,
        DateTime endOfDay,
        CancellationToken cancellationToken = default)
    {
        // 1. 查询到期落在指定区间的订阅（且未开启自动续费）
        var query = dbContext.TenantSubscriptions
            .Where(s => s.Status == SubscriptionStatus.Active
                        && !s.AutoRenew
                        && s.EffectiveTo >= startOfDay
                        && s.EffectiveTo < endOfDay)
            .Join(
                dbContext.Tenants,
                subscription => subscription.TenantId,
                tenant => tenant.Id,
                (subscription, tenant) => new { Subscription = subscription, Tenant = tenant })
            .Join(
                dbContext.TenantPackages,
                combined => combined.Subscription.TenantPackageId,
                package => package.Id,
                (combined, package) => new RenewalReminderCandidate
                {
                    Subscription = combined.Subscription,
                    Tenant = combined.Tenant,
                    Package = package
                });

        // 2. 返回候选列表
        return await query.ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<TenantSubscription>> FindExpiredActiveSubscriptionsAsync(
        DateTime now,
        CancellationToken cancellationToken = default)
    {
        // 1. 查询已到期仍为 Active 的订阅
        return await dbContext.TenantSubscriptions
            .Where(s => s.Status == SubscriptionStatus.Active && s.EffectiveTo < now)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<TenantSubscription>> FindGracePeriodExpiredSubscriptionsAsync(
        DateTime now,
        int gracePeriodDays,
        CancellationToken cancellationToken = default)
    {
        // 1. 查询宽限期已结束的订阅
        return await dbContext.TenantSubscriptions
            .Where(s => s.Status == SubscriptionStatus.GracePeriod
                        && s.EffectiveTo.AddDays(gracePeriodDays) < now)
            .ToListAsync(cancellationToken);
    }

    #endregion

    #region 套餐查询

    /// <inheritdoc />
    public async Task<TenantPackage?> FindPackageByIdAsync(long packageId, CancellationToken cancellationToken = default)
    {
        return await dbContext.TenantPackages
            .AsNoTracking()
            .FirstOrDefaultAsync(p => p.Id == packageId, cancellationToken);
    }

    #endregion

    #region 订阅更新

    /// <inheritdoc />
    public Task UpdateAsync(TenantSubscription subscription, CancellationToken cancellationToken = default)
    {
        dbContext.TenantSubscriptions.Update(subscription);
        return Task.CompletedTask;
    }

    #endregion

    #region 订阅历史

    /// <inheritdoc />
    public Task AddHistoryAsync(TenantSubscriptionHistory history, CancellationToken cancellationToken = default)
    {
        dbContext.TenantSubscriptionHistories.Add(history);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<SubscriptionHistoryWithPackageNames>> GetHistoryAsync(
        long subscriptionId,
        CancellationToken cancellationToken = default)
    {
        return await dbContext.TenantSubscriptionHistories
            .AsNoTracking()
            .Where(h => h.TenantSubscriptionId == subscriptionId)
            .OrderByDescending(h => h.CreatedAt)
            .Select(h => new SubscriptionHistoryWithPackageNames
            {
                History = h,
                FromPackageName = dbContext.TenantPackages
                    .Where(p => p.Id == h.FromPackageId)
                    .Select(p => p.Name)
                    .FirstOrDefault() ?? "",
                ToPackageName = dbContext.TenantPackages
                    .Where(p => p.Id == h.ToPackageId)
                    .Select(p => p.Name)
                    .FirstOrDefault() ?? ""
            })
            .ToListAsync(cancellationToken);
    }

    #endregion

    #region 配额使用

    /// <inheritdoc />
    public async Task<IReadOnlyList<TenantQuotaUsage>> GetQuotaUsagesAsync(
        long tenantId,
        CancellationToken cancellationToken = default)
    {
        return await dbContext.TenantQuotaUsages
            .AsNoTracking()
            .Where(q => q.TenantId == tenantId)
            .ToListAsync(cancellationToken);
    }

    #endregion

    #region 通知

    /// <inheritdoc />
    public Task AddNotificationAsync(TenantNotification notification, CancellationToken cancellationToken = default)
    {
        dbContext.TenantNotifications.Add(notification);
        return Task.CompletedTask;
    }

    #endregion

    #region 操作日志

    /// <inheritdoc />
    public Task AddOperationLogAsync(OperationLog log, CancellationToken cancellationToken = default)
    {
        logsContext.OperationLogs.Add(log);
        return Task.CompletedTask;
    }

    #endregion

    /// <inheritdoc />
    public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        // 1. 保存业务库变更
        await dbContext.SaveChangesAsync(cancellationToken);

        // 2. 保存日志库变更
        await logsContext.SaveChangesAsync(cancellationToken);
    }
}
