using Microsoft.EntityFrameworkCore;
using Tiku.Application.TenantAdmin;
using Tiku.Domain.Operations;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Security;
using CommerceRefundStatus = Tiku.Domain.Commerce.CommerceRefundStatus;
using OrderStatus = Tiku.Domain.Commerce.OrderStatus;
using ReconciliationIssueStatus = Tiku.Domain.Commerce.ReconciliationIssueStatus;

namespace Tiku.Infrastructure.TenantAdmin;

public sealed partial class TenantAdminDirectService
{
    public async Task<TenantAdminOverviewItem> GetOverviewAsync(
        TenantAdminActor actor,
        CancellationToken cancellationToken = default)
    {
        var scope = await RequireDataScopeAsync(actor, cancellationToken);
        var regionIds = scope.RegionIds.ToArray();
        var classIds = scope.ClassIds.ToArray();
        var now = DateTimeOffset.UtcNow;
        var today = new DateTimeOffset(now.UtcDateTime.Date, TimeSpan.Zero);
        var scopedClasses = dbContext.TenantClasses.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId)
            .ApplyDataScope(
                scope,
                item => item.CreatedBy == actor.UserId,
                item => classIds.Contains(item.Id) ||
                        (item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value)));
        var scopedStudents = dbContext.StudentProfiles.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId)
            .ApplyDataScope(
                scope,
                item => item.UserId == actor.UserId,
                item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));
        var scopedOrders = dbContext.Orders.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId)
            .ApplyDataScope(
                scope,
                item => item.UserId == actor.UserId,
                item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));

        var studentCount = await scopedStudents.CountAsync(cancellationToken);
        var classCount =
            await scopedClasses.CountAsync(item => item.Status == TenantRecordStatus.Active, cancellationToken);
        var staffCount = await dbContext.TenantMemberships.AsNoTracking()
            .CountAsync(item =>
                    item.TenantId == actor.TenantId &&
                    item.Status == MembershipStatus.Active &&
                    item.Role != TenantRole.Student,
                cancellationToken);
        var activePracticeCount = await dbContext.PracticeSessions.AsNoTracking()
            .CountAsync(item =>
                    item.TenantId == actor.TenantId &&
                    item.FinishedAt == null &&
                    (!item.ExpiresAt.HasValue || item.ExpiresAt > now),
                cancellationToken);
        var todayPracticeCount = await dbContext.PracticeSessions.AsNoTracking()
            .CountAsync(item => item.TenantId == actor.TenantId && item.StartedAt >= today, cancellationToken);
        var pendingFollowupCount = await dbContext.TenantStudentFollowups.AsNoTracking()
            .CountAsync(item =>
                    item.TenantId == actor.TenantId &&
                    (item.Status == StudentFollowupStatus.Open || item.Status == StudentFollowupStatus.InProgress),
                cancellationToken);
        var unreadNotificationCount = await dbContext.UserNotifications.AsNoTracking()
            .CountAsync(item => item.TenantId == actor.TenantId && item.Status == NotificationStatus.Unread,
                cancellationToken);
        var paidOrderCount = await scopedOrders.CountAsync(item => item.Status == OrderStatus.Paid, cancellationToken);
        var revenueCents = await scopedOrders
            .Where(item => item.Status == OrderStatus.Paid || item.Status == OrderStatus.PartiallyRefunded ||
                           item.Status == OrderStatus.Refunded)
            .SumAsync(item => item.AmountCents - item.RefundedAmountCents, cancellationToken);
        var pendingRefundCount = await dbContext.CommerceRefundRequests.AsNoTracking()
            .CountAsync(item =>
                    item.TenantId == actor.TenantId &&
                    (item.Status == CommerceRefundStatus.Requested ||
                     item.Status == CommerceRefundStatus.Approved ||
                     item.Status == CommerceRefundStatus.Processing),
                cancellationToken);
        var openReconciliationIssueCount = await dbContext.CommerceReconciliationIssues.AsNoTracking()
            .CountAsync(item =>
                    item.TenantId == actor.TenantId &&
                    item.Status != ReconciliationIssueStatus.Resolved &&
                    item.Status != ReconciliationIssueStatus.Ignored,
                cancellationToken);

        return new TenantAdminOverviewItem(
            studentCount,
            classCount,
            staffCount,
            activePracticeCount,
            todayPracticeCount,
            pendingFollowupCount,
            unreadNotificationCount,
            paidOrderCount,
            revenueCents,
            pendingRefundCount,
            openReconciliationIssueCount,
            now);
    }
}