using MediatR;
using Microsoft.EntityFrameworkCore;
using StorageService.Infrastructure.Persistence;

namespace StorageService.API.Application.Queries.Admin;

/// <summary>
/// EN: Handler for GetAllUsersQuotaQuery.
/// VI: Handler cho GetAllUsersQuotaQuery.
/// </summary>
public class GetAllUsersQuotaQueryHandler : IRequestHandler<GetAllUsersQuotaQuery, AllUsersQuotaResult>
{
    private readonly StorageServiceContext _context;

    public GetAllUsersQuotaQueryHandler(StorageServiceContext context)
    {
        _context = context;
    }

    public async Task<AllUsersQuotaResult> Handle(GetAllUsersQuotaQuery request, CancellationToken cancellationToken)
    {
        var query = _context.UserStorageQuotas.AsNoTracking();

        // EN: Apply filters / VI: Áp dụng filters
        if (!string.IsNullOrEmpty(request.QuotaTier))
        {
            query = query.Where(q => q.QuotaTier == request.QuotaTier);
        }

        if (request.MinUsagePercentage.HasValue)
        {
            query = query.Where(q => 
                q.MaxStorageBytes > 0 && 
                (double)q.UsedStorageBytes / q.MaxStorageBytes * 100 >= request.MinUsagePercentage.Value);
        }

        // EN: Get total count / VI: Lấy tổng số
        var totalCount = await query.CountAsync(cancellationToken);

        // EN: Apply sorting / VI: Áp dụng sắp xếp
        query = request.SortBy?.ToLower() switch
        {
            "usedstoragebytes" => request.Descending 
                ? query.OrderByDescending(q => q.UsedStorageBytes)
                : query.OrderBy(q => q.UsedStorageBytes),
            "maxstoragebytes" => request.Descending 
                ? query.OrderByDescending(q => q.MaxStorageBytes)
                : query.OrderBy(q => q.MaxStorageBytes),
            "currentfilecount" => request.Descending 
                ? query.OrderByDescending(q => q.CurrentFileCount)
                : query.OrderBy(q => q.CurrentFileCount),
            "lastupdatedat" => request.Descending 
                ? query.OrderByDescending(q => q.LastUpdatedAt)
                : query.OrderBy(q => q.LastUpdatedAt),
            _ => query.OrderByDescending(q => q.UsedStorageBytes) // Default: highest usage first
        };

        // EN: Apply pagination / VI: Áp dụng phân trang
        var skip = (request.PageNumber - 1) * request.PageSize;
        var items = await query
            .Skip(skip)
            .Take(request.PageSize)
            .Select(q => new AdminQuotaDto(
                q.Id,
                q.UserId,
                q.MaxStorageBytes,
                q.UsedStorageBytes,
                q.MaxStorageBytes - q.UsedStorageBytes,
                q.MaxFileCount,
                q.CurrentFileCount,
                q.MaxFileCount - q.CurrentFileCount,
                q.MaxStorageBytes > 0 ? Math.Round((double)q.UsedStorageBytes / q.MaxStorageBytes * 100, 2) : 0,
                q.QuotaTier,
                q.LastUpdatedAt,
                q.CreatedAt))
            .ToListAsync(cancellationToken);

        var totalPages = (int)Math.Ceiling((double)totalCount / request.PageSize);

        return new AllUsersQuotaResult(items, totalCount, request.PageNumber, request.PageSize, totalPages);
    }
}

/// <summary>
/// EN: Handler for GetStorageStatisticsQuery.
/// VI: Handler cho GetStorageStatisticsQuery.
/// </summary>
public class GetStorageStatisticsQueryHandler : IRequestHandler<GetStorageStatisticsQuery, StorageStatisticsDto>
{
    private readonly StorageServiceContext _context;

    public GetStorageStatisticsQueryHandler(StorageServiceContext context)
    {
        _context = context;
    }

    public async Task<StorageStatisticsDto> Handle(GetStorageStatisticsQuery request, CancellationToken cancellationToken)
    {
        // EN: Get quota statistics / VI: Lấy thống kê quota
        var quotaStats = await _context.UserStorageQuotas
            .AsNoTracking()
            .GroupBy(_ => 1)
            .Select(g => new
            {
                TotalUsers = g.Count(),
                TotalStorageUsedBytes = g.Sum(q => q.UsedStorageBytes),
                TotalStorageAllocatedBytes = g.Sum(q => q.MaxStorageBytes),
                TotalFileCount = g.Sum(q => q.CurrentFileCount),
                AverageUsagePercentage = g.Average(q => 
                    q.MaxStorageBytes > 0 ? (double)q.UsedStorageBytes / q.MaxStorageBytes * 100 : 0)
            })
            .FirstOrDefaultAsync(cancellationToken);

        // EN: Get users by tier / VI: Lấy users theo tier
        var usersByTier = await _context.UserStorageQuotas
            .AsNoTracking()
            .GroupBy(q => q.QuotaTier ?? "unknown")
            .Select(g => new { Tier = g.Key, Count = g.Count() })
            .ToDictionaryAsync(g => g.Tier, g => g.Count, cancellationToken);

        // EN: Get users near limit (>80% usage) / VI: Lấy users gần hết quota (>80%)
        var usersNearLimit = await _context.UserStorageQuotas
            .AsNoTracking()
            .Where(q => q.MaxStorageBytes > 0 && 
                       (double)q.UsedStorageBytes / q.MaxStorageBytes >= 0.8 &&
                       (double)q.UsedStorageBytes / q.MaxStorageBytes < 1.0)
            .CountAsync(cancellationToken);

        // EN: Get users over limit (>=100% usage) / VI: Lấy users vượt quota (>=100%)
        var usersOverLimit = await _context.UserStorageQuotas
            .AsNoTracking()
            .Where(q => q.MaxStorageBytes > 0 && 
                       (double)q.UsedStorageBytes / q.MaxStorageBytes >= 1.0)
            .CountAsync(cancellationToken);

        return new StorageStatisticsDto(
            quotaStats?.TotalUsers ?? 0,
            quotaStats?.TotalStorageUsedBytes ?? 0,
            quotaStats?.TotalStorageAllocatedBytes ?? 0,
            quotaStats?.TotalFileCount ?? 0,
            Math.Round(quotaStats?.AverageUsagePercentage ?? 0, 2),
            usersByTier,
            usersNearLimit,
            usersOverLimit);
    }
}

/// <summary>
/// EN: Handler for AdminGetFilesQuery.
/// VI: Handler cho AdminGetFilesQuery.
/// </summary>
public class AdminGetFilesQueryHandler : IRequestHandler<AdminGetFilesQuery, AdminFilesResult>
{
    private readonly StorageServiceContext _context;

    public AdminGetFilesQueryHandler(StorageServiceContext context)
    {
        _context = context;
    }

    public async Task<AdminFilesResult> Handle(AdminGetFilesQuery request, CancellationToken cancellationToken)
    {
        // EN: Include deleted files for admin / VI: Bao gồm files đã xóa cho admin
        var query = _context.StorageFiles
            .IgnoreQueryFilters()
            .AsNoTracking();

        // EN: Apply filters / VI: Áp dụng filters
        if (!string.IsNullOrEmpty(request.UserId))
        {
            query = query.Where(f => f.UserId == request.UserId);
        }

        if (!string.IsNullOrEmpty(request.AccessLevel))
        {
            query = query.Where(f => f.AccessLevel.ToString() == request.AccessLevel);
        }

        if (!string.IsNullOrEmpty(request.ContentType))
        {
            query = query.Where(f => f.ContentType.Contains(request.ContentType));
        }

        if (request.UploadedAfter.HasValue)
        {
            query = query.Where(f => f.UploadedAt >= request.UploadedAfter.Value);
        }

        if (request.UploadedBefore.HasValue)
        {
            query = query.Where(f => f.UploadedAt <= request.UploadedBefore.Value);
        }

        // EN: Get total count / VI: Lấy tổng số
        var totalCount = await query.CountAsync(cancellationToken);

        // EN: Apply sorting / VI: Áp dụng sắp xếp
        query = request.SortBy?.ToLower() switch
        {
            "filesizebytes" => request.Descending 
                ? query.OrderByDescending(f => f.FileSizeBytes)
                : query.OrderBy(f => f.FileSizeBytes),
            "uploadedat" => request.Descending 
                ? query.OrderByDescending(f => f.UploadedAt)
                : query.OrderBy(f => f.UploadedAt),
            "filename" => request.Descending 
                ? query.OrderByDescending(f => f.FileName)
                : query.OrderBy(f => f.FileName),
            _ => query.OrderByDescending(f => f.UploadedAt)
        };

        // EN: Apply pagination / VI: Áp dụng phân trang
        var skip = (request.PageNumber - 1) * request.PageSize;
        var items = await query
            .Skip(skip)
            .Take(request.PageSize)
            .Select(f => new AdminFileDto(
                f.Id,
                f.UserId,
                f.FileName,
                f.ContentType,
                f.FileSizeBytes,
                f.Provider.ToString(),
                f.AccessLevel.ToString(),
                null, // FolderId - not tracked in StorageFile directly
                f.UploadedAt,
                f.LastAccessedAt,
                f.IsDeleted))
            .ToListAsync(cancellationToken);

        var totalPages = (int)Math.Ceiling((double)totalCount / request.PageSize);

        return new AdminFilesResult(items, totalCount, request.PageNumber, request.PageSize, totalPages);
    }
}

/// <summary>
/// EN: Handler for AdminGetSharesQuery.
/// VI: Handler cho AdminGetSharesQuery.
/// </summary>
public class AdminGetSharesQueryHandler : IRequestHandler<AdminGetSharesQuery, AdminSharesResult>
{
    private readonly StorageServiceContext _context;

    public AdminGetSharesQueryHandler(StorageServiceContext context)
    {
        _context = context;
    }

    public async Task<AdminSharesResult> Handle(AdminGetSharesQuery request, CancellationToken cancellationToken)
    {
        var query = _context.FileShares.AsNoTracking();

        // EN: Apply filters / VI: Áp dụng filters
        if (!string.IsNullOrEmpty(request.Status))
        {
            query = query.Where(s => s.Status.ToString() == request.Status);
        }

        if (!string.IsNullOrEmpty(request.SharedBy))
        {
            query = query.Where(s => s.SharedBy == request.SharedBy);
        }

        // EN: Get total count / VI: Lấy tổng số
        var totalCount = await query.CountAsync(cancellationToken);

        // EN: Apply pagination / VI: Áp dụng phân trang
        var skip = (request.PageNumber - 1) * request.PageSize;
        var items = await query
            .OrderByDescending(s => s.CreatedAt)
            .Skip(skip)
            .Take(request.PageSize)
            .Select(s => new AdminShareDto(
                s.Id,
                s.FileId,
                s.SharedBy,
                s.SharedWith,
                s.Permission.ToString(),
                s.Status.ToString(),
                s.DownloadCount,
                s.MaxDownloads,
                s.ExpiresAt,
                s.CreatedAt,
                s.RevokedAt))
            .ToListAsync(cancellationToken);

        var totalPages = (int)Math.Ceiling((double)totalCount / request.PageSize);

        return new AdminSharesResult(items, totalCount, request.PageNumber, request.PageSize, totalPages);
    }
}
