using Microsoft.EntityFrameworkCore;
using Tiku.Application.PlatformAdmin;
using Tiku.Application.Security;
using Tiku.Domain.Platform;

namespace Tiku.Infrastructure.PlatformAdmin;

internal sealed partial class PlatformAdminService
{
    public async Task<PlatformAuditLogList> GetAuditLogsAsync(
        PlatformAdminActor actor,
        PlatformAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformAuditView, cancellationToken);
        return await ExecuteSystemAsync("platform audit log list", async dbContext =>
        {
            var logs = dbContext.AuditLogs.AsNoTracking();
            if (!string.IsNullOrWhiteSpace(query.Search))
            {
                var search = query.Search.Trim();
                logs = logs.Where(log =>
                    log.Action.Contains(search) || (log.TargetType != null && log.TargetType.Contains(search)));
            }

            return new PlatformAuditLogList(await logs
                .OrderByDescending(log => log.CreatedAt)
                .Take(Limit(query.Limit))
                .Select(log => new PlatformAuditLogItem(
                    log.Id,
                    log.TenantId,
                    log.ActorUserId,
                    log.Action,
                    log.TargetType,
                    log.TargetId,
                    log.Details,
                    log.IpAddress,
                    log.UserAgent,
                    log.CreatedAt))
                .ToArrayAsync(cancellationToken));
        }, cancellationToken);
    }

    public async Task<PlatformAuditAlertList> GetAuditAlertsAsync(
        PlatformAdminActor actor,
        PlatformAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformAuditView, cancellationToken);
        return await ExecuteSystemAsync("platform audit alert list", async dbContext =>
        {
            var alerts = dbContext.PlatformAuditAlerts.AsNoTracking();
            if (!string.IsNullOrWhiteSpace(query.Status))
                alerts = alerts.Where(alert => alert.Status == ParseAuditAlertStatus(query.Status));

            return new PlatformAuditAlertList(await alerts
                .OrderByDescending(alert => alert.LastSeenAt)
                .Take(Limit(query.Limit))
                .ToArrayAsync(cancellationToken));
        }, cancellationToken);
    }

    public async Task<PlatformAuditAlert> UpdateAuditAlertStatusAsync(
        PlatformAdminActor actor,
        UpdatePlatformAuditAlertStatusCommand command,
        CancellationToken cancellationToken = default)
    {
        await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformAuditView, cancellationToken);
        return await ExecuteSystemAsync("platform audit alert status update", async dbContext =>
        {
            var alert = await dbContext.PlatformAuditAlerts.SingleOrDefaultAsync(item => item.Id == command.AlertId,
                            cancellationToken)
                        ?? throw new PlatformAdminException("Platform audit alert was not found.",
                            "audit_alert_not_found");
            alert.Status = command.Status;
            alert.ResolutionNote = Normalize(command.ResolutionNote);
            if (command.Status == PlatformAuditAlertStatus.Acknowledged)
            {
                alert.AcknowledgedBy = actor.UserId;
                alert.AcknowledgedAt ??= DateTimeOffset.UtcNow;
            }
            else if (command.Status is PlatformAuditAlertStatus.Resolved or PlatformAuditAlertStatus.Ignored)
            {
                alert.ResolvedBy = actor.UserId;
                alert.ResolvedAt ??= DateTimeOffset.UtcNow;
            }

            AddAudit(dbContext, actor, "platform.audit_alert.status_changed", alert.Id,
                new { alert.Status, command.ResolutionNote });
            await dbContext.SaveChangesAsync(cancellationToken);
            return alert;
        }, cancellationToken);
    }
}