using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Tiku.Api.Contracts;
using Tiku.Application.Jobs;
using Tiku.Application.Security;
using Tiku.Domain.Operations;
using Tiku.Application.Assets;
using Tiku.Application.Storage;
using Tiku.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Tiku.Infrastructure.Storage;

namespace Tiku.Api.Controllers;

[ApiController]
[Tags("平台端-运维")]
[Route("api/platform-admin/operations")]
[Authorize(Policy = BackendPermissions.PlatformOperationsView)]
public sealed class PlatformOperationsController(
    IBackgroundJobService backgroundJobService,
    ICurrentUser currentUser,
    TikuDbContext dbContext,
    IRedisSecurityStore redisSecurityStore,
    IAssetSecurityScanner assetSecurityScanner,
    IObjectStorageService objectStorageService,
    IOptions<AliyunOssOptions> aliyunOssOptions) : ControllerBase
{
    [HttpGet("health")]
    [EndpointSummary("查询受保护的依赖深度健康状态")]
    public async Task<ActionResult<object>> Health(CancellationToken cancellationToken)
    {
        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 newestHeartbeat = await dbContext.WorkerHeartbeats.AsNoTracking()
            .MaxAsync(item => (DateTimeOffset?)item.LastHeartbeatAt, cancellationToken);
        var workerReady = newestHeartbeat >= DateTimeOffset.UtcNow.AddMinutes(-2);
        return Ok(new
        {
            status = database && redis && clamAv && workerReady && storageConfigured ? "healthy" : "degraded",
            database,
            redis = new { configured = redisSecurityStore.IsConfigured, ready = redis },
            worker = new { ready = workerReady, lastHeartbeatAt = newestHeartbeat },
            clamAv,
            storage = new { provider = storageProvider, configured = storageConfigured },
            checkedAt = DateTimeOffset.UtcNow
        });
    }

    [HttpGet("workers")]
    [EndpointSummary("查询 Worker 与周期循环状态")]
    public async Task<ActionResult<object>> Workers(CancellationToken cancellationToken)
    {
        var now = DateTimeOffset.UtcNow;
        var items = await dbContext.WorkerHeartbeats.AsNoTracking()
            .OrderBy(item => item.WorkerId)
            .ThenBy(item => item.Processor)
            .Select(item => new
            {
                item.WorkerId,
                item.Processor,
                item.StartedAt,
                item.LastHeartbeatAt,
                item.LastIterationStartedAt,
                item.LastIterationCompletedAt,
                item.LastSucceededAt,
                item.LastError,
                item.IsRunning
            })
            .ToArrayAsync(cancellationToken);
        return Ok(new
        {
            staleAfterSeconds = 120,
            items = items.Select(item => new
            {
                item.WorkerId,
                item.Processor,
                item.StartedAt,
                item.LastHeartbeatAt,
                item.LastIterationStartedAt,
                item.LastIterationCompletedAt,
                item.LastSucceededAt,
                item.LastError,
                item.IsRunning,
                stale = item.LastHeartbeatAt < now.AddMinutes(-2)
            })
        });
    }

    [HttpGet("job-metrics")]
    [EndpointSummary("查询后台任务队列指标")]
    public async Task<ActionResult<object>> JobMetrics(CancellationToken cancellationToken)
    {
        var now = DateTimeOffset.UtcNow;
        var counts = await dbContext.BackgroundJobs.AsNoTracking()
            .GroupBy(item => item.Status)
            .Select(group => new { status = group.Key, count = group.Count() })
            .ToArrayAsync(cancellationToken);
        var oldestPending = await dbContext.BackgroundJobs.AsNoTracking()
            .Where(item => item.Status == BackgroundJobStatus.Pending)
            .MinAsync(item => (DateTimeOffset?)item.CreatedAt, cancellationToken);
        var expiredLeases = await dbContext.BackgroundJobs.AsNoTracking()
            .CountAsync(item => item.Status == BackgroundJobStatus.Processing && item.LockExpiresAt < now, cancellationToken);
        return Ok(new
        {
            counts,
            oldestPendingAt = oldestPending,
            queueAgeSeconds = oldestPending.HasValue ? Math.Max(0, (now - oldestPending.Value).TotalSeconds) : 0,
            expiredLeases,
            checkedAt = now
        });
    }

    [HttpGet("jobs")]
    [EndpointSummary("查询全平台后台任务")]
    public async Task<ActionResult<IReadOnlyCollection<BackgroundJobItem>>> Jobs(
        [FromQuery] Guid? tenantId,
        [FromQuery] string? jobType,
        [FromQuery] BackgroundJobStatus? status,
        [FromQuery] int? limit,
        CancellationToken cancellationToken) =>
        Ok(await backgroundJobService.ListPlatformAsync(
            tenantId, jobType, status, limit ?? 100, cancellationToken));

    [HttpGet("jobs/{jobId:guid}")]
    [EndpointSummary("查询平台后台任务详情")]
    public async Task<ActionResult<BackgroundJobItem>> Job(Guid jobId, CancellationToken cancellationToken)
    {
        var item = await backgroundJobService.GetAsync(jobId, null, cancellationToken);
        return item is null ? NotFound() : Ok(item);
    }

    [HttpPost("jobs/{jobId:guid}/cancel")]
    [Authorize(Policy = BackendPermissions.PlatformOperationsManage)]
    [EndpointSummary("取消平台后台任务")]
    public async Task<ActionResult<BackgroundJobItem>> CancelJob(
        Guid jobId,
        CancelBackgroundJobDto request,
        CancellationToken cancellationToken) =>
        Ok(await backgroundJobService.RequestCancellationAsync(
            jobId, null, ResolveUserId(), request.Reason, cancellationToken));

    [HttpPost("jobs/{jobId:guid}/retry")]
    [Authorize(Policy = BackendPermissions.PlatformOperationsManage)]
    [EndpointSummary("重试平台后台任务")]
    public async Task<ActionResult<BackgroundJobItem>> RetryJob(Guid jobId, CancellationToken cancellationToken) =>
        Ok(await backgroundJobService.RetryAsync(jobId, null, ResolveUserId(), cancellationToken));

    private Guid ResolveUserId() =>
        currentUser.UserId ?? throw new InvalidOperationException("Current platform user was not resolved.");
}
