using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Tiku.Application.Assets;
using Tiku.Application.PlatformAdmin.Operations;
using Tiku.Application.Security;
using Tiku.Application.Storage;
using Tiku.Domain.Operations;
using Tiku.Domain.Platform;
using Tiku.Infrastructure.Persistence;
using Tiku.Infrastructure.Storage;

namespace Tiku.Infrastructure.PlatformAdmin.Operations;

internal sealed class PlatformOperationsQueryService(
    TikuDbContext dbContext,
    IRedisSecurityStore redisSecurityStore,
    IAssetSecurityScanner assetSecurityScanner,
    IObjectStorageService objectStorageService,
    IOptions<AliyunOssOptions> aliyunOssOptions) : IPlatformOperationsQueryService
{
    public async Task<PlatformDependencyHealth> GetHealthAsync(CancellationToken cancellationToken = default)
    {
        var database = await dbContext.Database.CanConnectAsync(cancellationToken);
        var redis = !redisSecurityStore.IsConfigured || await redisSecurityStore.PingAsync(cancellationToken);
        var clamAv = await assetSecurityScanner.CheckHealthAsync(cancellationToken);
        var storageProvider = objectStorageService.ConfiguredDefaultProvider();
        var storageConfigured = storageProvider switch
        {
            ObjectStorageProviders.AliyunOss => aliyunOssOptions.Value.IsConfigured,
            ObjectStorageProviders.LocalDev => true,
            _ => false
        };
        var heartbeat = await dbContext.WorkerHeartbeats.AsNoTracking()
            .MaxAsync(item => (DateTimeOffset?)item.LastHeartbeatAt, cancellationToken);
        var workerReady = heartbeat >= DateTimeOffset.UtcNow.AddMinutes(-2);
        return new PlatformDependencyHealth(
            database && redis && clamAv && workerReady && storageConfigured,
            database,
            redisSecurityStore.IsConfigured,
            redis,
            workerReady,
            heartbeat,
            clamAv,
            storageProvider,
            storageConfigured,
            DateTimeOffset.UtcNow);
    }

    public async Task<IReadOnlyCollection<PlatformWorkerState>> GetWorkersAsync(
        CancellationToken cancellationToken = default)
    {
        var staleBefore = DateTimeOffset.UtcNow.AddMinutes(-2);
        return await dbContext.WorkerHeartbeats.AsNoTracking()
            .OrderBy(item => item.WorkerId)
            .ThenBy(item => item.Processor)
            .Select(item => new PlatformWorkerState(
                item.WorkerId,
                item.Processor,
                item.StartedAt,
                item.LastHeartbeatAt,
                item.LastIterationStartedAt,
                item.LastIterationCompletedAt,
                item.LastSucceededAt,
                item.LastError,
                item.IsRunning,
                item.LastHeartbeatAt < staleBefore))
            .ToArrayAsync(cancellationToken);
    }

    public async Task<PlatformJobMetrics> GetJobMetricsAsync(CancellationToken cancellationToken = default)
    {
        var now = DateTimeOffset.UtcNow;
        var counts = await dbContext.BackgroundJobs.AsNoTracking()
            .GroupBy(item => item.Status)
            .Select(group => new PlatformMetricCount(group.Key.ToString(), group.Count()))
            .ToArrayAsync(cancellationToken);
        var oldest = await dbContext.BackgroundJobs.AsNoTracking()
            .Where(item => item.Status == BackgroundJobStatus.Pending)
            .MinAsync(item => (DateTimeOffset?)item.CreatedAt, cancellationToken);
        var expired = await dbContext.BackgroundJobs.AsNoTracking()
            .CountAsync(item => item.Status == BackgroundJobStatus.Processing && item.LockExpiresAt < now,
                cancellationToken);
        return new PlatformJobMetrics(
            counts,
            oldest,
            oldest.HasValue ? Math.Max(0, (now - oldest.Value).TotalSeconds) : 0,
            expired,
            now);
    }

    public async Task<PlatformGovernanceMetrics> GetGovernanceMetricsAsync(
        CancellationToken cancellationToken = default)
    {
        var now = DateTimeOffset.UtcNow;
        var approvals = await dbContext.PlatformApprovalRequests.AsNoTracking()
            .GroupBy(item => item.Status)
            .Select(group => new PlatformMetricCount(group.Key.ToString(), group.Count()))
            .ToArrayAsync(cancellationToken);
        var expired = await dbContext.PlatformApprovalRequests.AsNoTracking()
            .CountAsync(item => item.Status == PlatformApprovalRequestStatus.Pending && item.ExpiresAt <= now,
                cancellationToken);
        var drafts = await dbContext.PlatformConfigurationVersions.AsNoTracking()
            .CountAsync(item => item.Status == PlatformConfigurationVersionStatus.Draft, cancellationToken);
        var notifications = await dbContext.PlatformNotificationDeliveries.AsNoTracking()
            .GroupBy(item => item.Status)
            .Select(group => new PlatformMetricCount(group.Key.ToString(), group.Count()))
            .ToArrayAsync(cancellationToken);
        return new PlatformGovernanceMetrics(approvals, expired, drafts, notifications, now);
    }
}