namespace DodoSSH.Contracts;

/// <summary>
/// One entry in the append-only log of every identity key statement ever published.
/// </summary>
/// <remarks>
/// <para>
/// Served so a client can recompute the chain for itself rather than taking the directory's word for a
/// public key. Every field the hash covers is here, in the order docs/crypto.md §7.2 hashes them, so
/// verification is <c>ComputeEntryHash(previous, …) == hash</c> and nothing else.
/// </para>
/// <para>
/// <see cref="Sequence"/> is deliberately <em>not</em> an input to the hash. It is assigned by the
/// database on insert, so a renumbered or gapped column cannot silently reorder history — order
/// follows the hash links. It is here to page with, and to compare against what a directory entry
/// claims.
/// </para>
/// </remarks>
/// <param name="Sequence">Monotonic position in the log.</param>
/// <param name="UserId">Whose key this is.</param>
/// <param name="Generation">Which generation of that user's key.</param>
/// <param name="EncryptionPublicKey">X25519 public key, 32 bytes.</param>
/// <param name="SigningPublicKey">Ed25519 public key, 32 bytes.</param>
/// <param name="StatementSignature">The self-signature over the key statement.</param>
/// <param name="PreviousHash">Hash of the preceding entry; all-zero for the first.</param>
/// <param name="Hash">This entry's hash, over the previous hash and its own contents.</param>
/// <param name="CreatedAt">When it was appended, truncated to milliseconds as the hash requires.</param>
public sealed record KeyLogRecord(
    long Sequence,
    Guid UserId,
    int Generation,
    byte[] EncryptionPublicKey,
    byte[] SigningPublicKey,
    byte[] StatementSignature,
    byte[] PreviousHash,
    byte[] Hash,
    DateTimeOffset CreatedAt);

/// <summary>A page of the key log, with the head as of this response.</summary>
/// <remarks>
/// <para>
/// The head is what a client records in every grant it signs, which is what makes a forked view
/// detectable: for a server to show two clients different key logs it must keep both forks consistent
/// across every later shared operation, and any two clients touching one vault then disagree. It
/// converts an otherwise invisible key substitution into a visible one. It does not prevent it — see
/// ADR 0001.
/// </para>
/// <para>
/// <see cref="Head"/> describes the whole log, not this page: a client that pages from an old cursor
/// still learns where the end is, and can tell whether it has caught up without a second call.
/// </para>
/// </remarks>
/// <param name="Entries">Entries after the requested sequence, in ascending order.</param>
/// <param name="HeadSequence">Sequence of the log's last entry; 0 when the log is empty.</param>
/// <param name="Head">
/// Hash of the log's last entry. All-zero when the log is empty, which is the same value the first
/// entry records as its predecessor.
/// </param>
/// <param name="HasMore">Whether entries beyond this page are immediately available.</param>
public sealed record KeyLogPage(
    IReadOnlyList<KeyLogRecord> Entries,
    long HeadSequence,
    byte[] Head,
    bool HasMore);
