using System.Text.Json; using ItWorx.KnowledgeHub.Application.Audit; using ItWorx.KnowledgeHub.Application.Common; using ItWorx.KnowledgeHub.Application.Infrastructure.Validation; using ItWorx.KnowledgeHub.Domain.Infrastructure; namespace ItWorx.KnowledgeHub.Application.Infrastructure.UseCases; public sealed record SetManualOverrideCommand( Guid WorkspaceId, InfrastructureObjectKind TargetKind, Guid TargetId, string FieldName, string ValueJson, string? Reason, Guid ActorId, string? CorrelationId); public sealed class SetManualOverrideUseCase( IInfrastructureRepository infrastructure, IAuditEventRecorder auditEvents, TimeProvider timeProvider) { public async Task> ExecuteAsync(SetManualOverrideCommand command, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(command.FieldName) || string.IsNullOrWhiteSpace(command.ValueJson)) { return OperationResultValue.Failure(OperationStatus.ValidationFailed, ["Veldnaam en overridewaarde zijn verplicht."]); } JsonElement value; try { using var document = JsonDocument.Parse(command.ValueJson); value = document.RootElement.Clone(); } catch (JsonException) { return OperationResultValue.Failure(OperationStatus.ValidationFailed, ["Overridewaarde is geen geldige JSON."]); } var catalogErrors = ManualOverrideCatalog.Validate(command.TargetKind, command.FieldName.Trim(), value, command.Reason); if (catalogErrors.Count > 0) { return OperationResultValue.Failure(OperationStatus.ValidationFailed, catalogErrors); } if (!await infrastructure.TargetExistsAsync(command.WorkspaceId, command.TargetKind, command.TargetId, cancellationToken)) { return OperationResultValue.Failure(OperationStatus.NotFound); } var now = timeProvider.GetUtcNow(); var manualOverride = await infrastructure.FindOverrideAsync( command.WorkspaceId, command.TargetKind, command.TargetId, command.FieldName.Trim(), cancellationToken); if (manualOverride is null) { manualOverride = ManualOverride.Create( command.WorkspaceId, command.TargetKind, command.TargetId, command.FieldName, command.ValueJson, command.Reason, command.ActorId, now); infrastructure.AddOverride(manualOverride); } else { manualOverride.Replace(command.ValueJson, command.Reason, command.ActorId, now); } await infrastructure.SaveChangesAsync(cancellationToken); await auditEvents.RecordAsync( new AuditEventRecord( AuditActorKind.User, command.ActorId, "web", "infrastructure.override-set", command.TargetKind.ToString().ToLowerInvariant(), command.TargetId.ToString(), $"Handmatige correctie voor veld '{command.FieldName.Trim()}' opgeslagen.", command.CorrelationId, now), cancellationToken); return OperationResultValue.Success(manualOverride.Id); } } public sealed class RemoveManualOverrideUseCase( IInfrastructureRepository infrastructure, IAuditEventRecorder auditEvents, TimeProvider timeProvider) { public async Task ExecuteAsync( Guid workspaceId, Guid overrideId, Guid actorId, string? correlationId, CancellationToken cancellationToken) { try { await infrastructure.RemoveOverrideAsync(workspaceId, overrideId, cancellationToken); await infrastructure.SaveChangesAsync(cancellationToken); } catch (KeyNotFoundException) { return OperationResult.Failure(OperationStatus.NotFound); } await auditEvents.RecordAsync( new AuditEventRecord( AuditActorKind.User, actorId, "web", "infrastructure.override-removed", "manual-override", overrideId.ToString(), "Handmatige correctie verwijderd.", correlationId, timeProvider.GetUtcNow()), cancellationToken); return OperationResult.Success(); } }