using static StellaOps.Localization.T; namespace StellaOps.Evidence.Bundle; /// Builder for constructing evidence bundles. public sealed class EvidenceBundleBuilder { private readonly TimeProvider _timeProvider; private string? _alertId; private string? _artifactId; private ReachabilityEvidence? _reachability; private CallStackEvidence? _callStack; private ProvenanceEvidence? _provenance; private VexStatusEvidence? _vexStatus; private DiffEvidence? _diff; private GraphRevisionEvidence? _graphRevision; // Sprint: SPRINT_20260112_008_LB_binary_diff_evidence_models (BINDIFF-LB-002) private BinaryDiffEvidence? _binaryDiff; public EvidenceBundleBuilder(TimeProvider timeProvider) => _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider)); public EvidenceBundleBuilder() : this(TimeProvider.System) { } public EvidenceBundleBuilder WithAlertId(string alertId) { _alertId = alertId; return this; } public EvidenceBundleBuilder WithArtifactId(string artifactId) { _artifactId = artifactId; return this; } public EvidenceBundleBuilder WithReachability(ReachabilityEvidence e) { _reachability = e; return this; } public EvidenceBundleBuilder WithCallStack(CallStackEvidence e) { _callStack = e; return this; } public EvidenceBundleBuilder WithProvenance(ProvenanceEvidence e) { _provenance = e; return this; } public EvidenceBundleBuilder WithVexStatus(VexStatusEvidence e) { _vexStatus = e; return this; } public EvidenceBundleBuilder WithDiff(DiffEvidence e) { _diff = e; return this; } public EvidenceBundleBuilder WithGraphRevision(GraphRevisionEvidence e) { _graphRevision = e; return this; } // BINDIFF-LB-002: Add binary diff builder method public EvidenceBundleBuilder WithBinaryDiff(BinaryDiffEvidence e) { _binaryDiff = e; return this; } public EvidenceBundle Build() { if (string.IsNullOrWhiteSpace(_alertId)) throw new InvalidOperationException(_t("common.evidence.bundle_alert_id_required")); if (string.IsNullOrWhiteSpace(_artifactId)) throw new InvalidOperationException(_t("common.evidence.bundle_artifact_id_required")); var hashes = new Dictionary(); if (_reachability?.Hash is not null) hashes["reachability"] = _reachability.Hash; if (_callStack?.Hash is not null) hashes["callstack"] = _callStack.Hash; if (_provenance?.Hash is not null) hashes["provenance"] = _provenance.Hash; if (_vexStatus?.Hash is not null) hashes["vex"] = _vexStatus.Hash; if (_diff?.Hash is not null) hashes["diff"] = _diff.Hash; if (_graphRevision?.Hash is not null) hashes["graph"] = _graphRevision.Hash; // BINDIFF-LB-002: Include binary diff hash if (_binaryDiff?.Hash is not null) hashes["binaryDiff"] = _binaryDiff.Hash; return new EvidenceBundle { AlertId = _alertId, ArtifactId = _artifactId, Reachability = _reachability, CallStack = _callStack, Provenance = _provenance, VexStatus = _vexStatus, Diff = _diff, GraphRevision = _graphRevision, BinaryDiff = _binaryDiff, Hashes = hashes.Count > 0 ? EvidenceHashSet.Compute(hashes) : EvidenceHashSet.Empty(), CreatedAt = _timeProvider.GetUtcNow() }; } }