using DodoSSH.Domain;
using Microsoft.EntityFrameworkCore;

namespace DodoSSH.Infrastructure;

/// <summary>
/// The application database context.
/// </summary>
/// <remarks>
/// <para>
/// Everything lives in the <c>dodo</c> schema with snake_case names. Timestamps are
/// <c>timestamptz</c> and always UTC, so there is no tzdata dependency and no conflict with
/// <c>InvariantGlobalization</c>.
/// </para>
/// <para>
/// Two concurrency mechanisms coexist deliberately. <c>Version</c> on item rows is the
/// client-visible, monotonic value used for <c>expectedVersion</c> conflict detection.
/// <c>xmin</c> is the server-side optimistic guard and is never exposed, because it is not stable
/// across <c>VACUUM FREEZE</c> and must never become a client cursor.
/// </para>
/// </remarks>
public class DodoDbContext(DbContextOptions<DodoDbContext> options) : DbContext(options)
{
    /// <summary>The database schema every table lives in.</summary>
    public const string SchemaName = "dodo";

    /// <summary>User accounts.</summary>
    public DbSet<UserAccount> Users => Set<UserAccount>();

    /// <summary>Identity key generations.</summary>
    public DbSet<UserKey> UserKeys => Set<UserKey>();

    /// <summary>Wraps of users' secret bundles.</summary>
    public DbSet<UserKeyWrap> UserKeyWraps => Set<UserKeyWrap>();

    /// <summary>Enrolled devices.</summary>
    public DbSet<Device> Devices => Set<Device>();

    /// <summary>The append-only key transparency log.</summary>
    public DbSet<KeyLogEntry> KeyLog => Set<KeyLogEntry>();

    /// <summary>Teams.</summary>
    public DbSet<Team> Teams => Set<Team>();

    /// <summary>Team memberships.</summary>
    public DbSet<TeamMembership> TeamMemberships => Set<TeamMembership>();

    /// <summary>Vaults.</summary>
    public DbSet<Vault> Vaults => Set<Vault>();

    /// <summary>Wrapped vault keys.</summary>
    public DbSet<VaultKeyGrant> VaultKeyGrants => Set<VaultKeyGrant>();

    /// <summary>SSH hosts.</summary>
    public DbSet<SshHost> Hosts => Set<SshHost>();

    /// <summary>SSH key pairs, held as ciphertext.</summary>
    public DbSet<VaultSshKey> SshKeys => Set<VaultSshKey>();

    /// <summary>Usernames and passwords, held as ciphertext.</summary>
    public DbSet<VaultCredential> Credentials => Set<VaultCredential>();

    /// <summary>Trusted SSH host keys, held as ciphertext.</summary>
    public DbSet<VaultKnownHostKey> KnownHostKeys => Set<VaultKnownHostKey>();

    /// <summary>Host groups, held as ciphertext.</summary>
    public DbSet<VaultHostGroup> HostGroups => Set<VaultHostGroup>();

    /// <summary>Tags hosts can wear, held as ciphertext.</summary>
    public DbSet<VaultTag> Tags => Set<VaultTag>();

    /// <summary>Saved commands, held as ciphertext.</summary>
    public DbSet<VaultSnippet> Snippets => Set<VaultSnippet>();

    /// <summary>Connections that were made, held as ciphertext.</summary>
    public DbSet<VaultConnectionLogEntry> ConnectionLog => Set<VaultConnectionLogEntry>();

    /// <summary>Changes made to keychain items, held as ciphertext.</summary>
    public DbSet<VaultActivityLogEntry> ActivityLog => Set<VaultActivityLogEntry>();

    /// <summary>S3-compatible buckets and their credentials, held as ciphertext.</summary>
    public DbSet<VaultObjectStore> ObjectStores => Set<VaultObjectStore>();

    /// <summary>The per-vault change log that delta sync reads.</summary>
    public DbSet<VaultChange> VaultChanges => Set<VaultChange>();

    /// <summary>Applied-operation receipts, for exactly-once retries.</summary>
    public DbSet<SyncOperationReceipt> SyncOperationReceipts => Set<SyncOperationReceipt>();

    /// <inheritdoc />
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        ArgumentNullException.ThrowIfNull(modelBuilder);

        modelBuilder.HasDefaultSchema(SchemaName);
        modelBuilder.HasPostgresExtension("citext");

        modelBuilder.ApplyConfigurationsFromAssembly(typeof(DodoDbContext).Assembly);

        base.OnModelCreating(modelBuilder);
    }
}
