using System;
using Svrnty.CQRS.Events.Abstractions.Models;
using Svrnty.CQRS.Events.Abstractions.EventStore;
namespace Svrnty.CQRS.Events.Abstractions.Models;
///
/// Represents schema information for a versioned event type.
///
///
///
/// Schema information tracks the evolution of event types over time, enabling:
/// - Automatic upcasting from old versions to new versions
/// - JSON schema generation for external consumers
/// - Version compatibility checking
///
///
/// Example:
/// UserCreatedEventV1 → UserCreatedEventV2 (added Email property)
///
///
/// The fully qualified CLR type name of the event (e.g., "MyApp.UserCreatedEvent")
/// The semantic version of this schema (e.g., 1, 2, 3)
/// The .NET Type that represents this version
/// JSON Schema (Draft 7) describing the event structure (optional, for external consumers)
/// The CLR type of the previous version (null for version 1)
/// The version number this version can upcast from (null for version 1)
/// When this schema was registered in the system
public sealed record SchemaInfo(
string EventType,
int Version,
Type ClrType,
string? JsonSchema,
Type? UpcastFromType,
int? UpcastFromVersion,
DateTimeOffset RegisteredAt)
{
///
/// Gets a value indicating whether this is the initial version of the event.
///
public bool IsInitialVersion => Version == 1 && UpcastFromType == null;
///
/// Gets a value indicating whether this schema can be upcast from a previous version.
///
public bool SupportsUpcasting => UpcastFromType != null && UpcastFromVersion.HasValue;
///
/// Gets the schema identifier (EventType:Version).
///
public string SchemaId => $"{EventType}:v{Version}";
///
/// Validates the schema information for correctness.
///
/// Thrown if the schema info is invalid.
public void Validate()
{
if (string.IsNullOrWhiteSpace(EventType))
throw new InvalidOperationException("EventType cannot be null or whitespace.");
if (Version < 1)
throw new InvalidOperationException($"Version must be >= 1, got {Version}.");
if (ClrType == null)
throw new InvalidOperationException("ClrType cannot be null.");
if (!ClrType.IsAssignableTo(typeof(ICorrelatedEvent)))
throw new InvalidOperationException($"ClrType {ClrType.FullName} must implement ICorrelatedEvent.");
// Version 1 should not have upcast information
if (Version == 1)
{
if (UpcastFromType != null)
throw new InvalidOperationException("Version 1 should not have UpcastFromType.");
if (UpcastFromVersion.HasValue)
throw new InvalidOperationException("Version 1 should not have UpcastFromVersion.");
}
else
{
// Versions > 1 should have upcast information
if (UpcastFromType == null)
throw new InvalidOperationException($"Version {Version} must specify UpcastFromType.");
if (!UpcastFromVersion.HasValue)
throw new InvalidOperationException($"Version {Version} must specify UpcastFromVersion.");
if (UpcastFromVersion.Value != Version - 1)
throw new InvalidOperationException(
$"Version {Version} must upcast from version {Version - 1}, got {UpcastFromVersion.Value}.");
}
}
}