using Insight.Database; using Npgsql; using PlanTempus.Components.Accounts.Exceptions; using PlanTempus.Core; using PlanTempus.Core.CommandQueries; using PlanTempus.Core.Database; using PlanTempus.Core.Outbox; namespace PlanTempus.Components.Accounts.Create { public class CreateAccountHandler( IDatabaseOperations databaseOperations, ISecureTokenizer secureTokenizer, IOutboxService outboxService) : ICommandHandler { public async Task Handle(CreateAccountCommand command) { using var db = databaseOperations.CreateScope(nameof(CreateAccountHandler)); using var transaction = db.Connection.BeginTransaction(); try { var securityStamp = Guid.NewGuid().ToString("N"); var sql = @" INSERT INTO system.accounts(email, password_hash, security_stamp, email_confirmed, access_failed_count, lockout_enabled, is_active) VALUES(@Email, @PasswordHash, @SecurityStamp, @EmailConfirmed, @AccessFailedCount, @LockoutEnabled, @IsActive) RETURNING id, created_at, email, is_active"; await db.Connection.QuerySqlAsync(sql, new { command.Email, PasswordHash = secureTokenizer.TokenizeText(command.Password), SecurityStamp = securityStamp, EmailConfirmed = false, AccessFailedCount = 0, LockoutEnabled = false, command.IsActive, }); await outboxService.EnqueueAsync( OutboxMessageTypes.VerificationEmail, new VerificationEmailPayload { Email = command.Email, UserName = command.Email, Token = securityStamp }, db.Connection, transaction); transaction.Commit(); return new CommandResponse(command.CorrelationId, command.GetType().Name, command.TransactionId); } catch (PostgresException ex) when (ex.SqlState == "23505" && ex.ConstraintName.Equals("accounts_email_key", StringComparison.InvariantCultureIgnoreCase)) { transaction.Rollback(); db.Error(ex); throw new EmailAlreadyRegistreredException(); } catch (Exception ex) { transaction.Rollback(); db.Error(ex); throw; } } } }