using ZA.CoreService.ESBCertificateManager.Models; namespace ZA.CoreService.ESBCertificateManager.Setup; public sealed class SetupWizardForm : Form { private readonly SetupCoordinator _coordinator; private readonly Color _backgroundColor = Color.FromArgb(10, 18, 34); private readonly Color _cardColor = Color.FromArgb(16, 38, 65); private readonly Color _textColor = Color.FromArgb(241, 245, 249); private readonly Color _mutedTextColor = Color.FromArgb(169, 184, 200); private readonly Color _goldColor = Color.FromArgb(208, 171, 57); private readonly Color _greenColor = Color.FromArgb(60, 203, 127); private readonly Color _redColor = Color.FromArgb(239, 106, 106); private Label _lblRuntime = null!; private Label _lblCertificate = null!; private Label _lblDatabase = null!; private Label _lblSelection = null!; private Label _lblEnvironment = null!; private Label _lblSystemDetails = null!; private ComboBox _cmbSystem = null!; private ComboBox _cmbCredential = null!; private Button _btnImportCertificate = null!; private Button _btnRefresh = null!; private Button _btnSaveSelection = null!; private Button _btnFinish = null!; private Button _btnAddCredential = null!; private IReadOnlyList _systems = []; private IReadOnlyList _profiles = []; private bool _running; public SetupWizardForm( SetupCoordinator coordinator) { _coordinator = coordinator; BuildInterface(); Shown += async (_, _) => await InitializeAsync(); } private void BuildInterface() { Text = "ESB Certificate Manager einrichten"; StartPosition = FormStartPosition.CenterScreen; Size = new Size(950, 690); MinimumSize = new Size(900, 650); MaximizeBox = false; BackColor = _backgroundColor; ForeColor = _textColor; Font = new Font("Segoe UI", 10); Label title = new() { Text = "Ersteinrichtung", AutoSize = true, Location = new Point(38, 25), ForeColor = _textColor, Font = new Font( "Segoe UI", 24, FontStyle.Bold) }; Label subtitle = new() { Text = "Runtime, Datenbank, Zertifikat und Systemauswahl prüfen.", AutoSize = true, Location = new Point(42, 70), ForeColor = _mutedTextColor }; Panel checkCard = CreateCard( new Point(38, 105), new Size(870, 225)); Label checkTitle = CreateTitle( "Systemprüfung", new Point(20, 16)); _lblRuntime = CreateStatus( new Point(20, 55)); _lblCertificate = CreateStatus( new Point(20, 95)); _lblDatabase = CreateStatus( new Point(20, 135)); _lblSelection = CreateStatus( new Point(20, 175)); _btnImportCertificate = CreateButton( "PFX importieren"); _btnImportCertificate.Location = new Point(680, 90); _btnImportCertificate.Size = new Size(160, 38); _btnImportCertificate.Click += async (_, _) => await ImportCertificateAsync(); _cmbCredential = new ComboBox { Location = new Point(185, 133), Size = new Size(300, 30), DropDownStyle = ComboBoxStyle.DropDownList }; checkCard.Controls.Add(checkTitle); checkCard.Controls.Add(_lblRuntime); checkCard.Controls.Add(_lblCertificate); checkCard.Controls.Add(_lblDatabase); checkCard.Controls.Add(_lblSelection); checkCard.Controls.Add(_btnImportCertificate); Panel selectionCard = CreateCard( new Point(38, 350), new Size(870, 205)); Label selectionTitle = CreateTitle( "System auswählen", new Point(20, 16)); _lblEnvironment = new Label { Text = $"Umgebung: {_coordinator.Settings.EnvironmentCode}", AutoSize = true, Location = new Point(22, 55), ForeColor = _goldColor, Font = new Font( "Segoe UI", 10, FontStyle.Bold) }; Label systemLabel = CreateLabel( "Sonic-System", new Point(22, 92)); _cmbSystem = new ComboBox { Location = new Point(185, 88), Size = new Size(300, 30), DropDownStyle = ComboBoxStyle.DropDownList }; _cmbSystem.SelectedIndexChanged += async (_, _) => await SystemChangedAsync(); Label credentialLabel = CreateLabel( "Benutzerprofil", new Point(22, 137)); _cmbCredential = new ComboBox { Location = new Point(185, 133), Size = new Size(300, 30), DropDownStyle = ComboBoxStyle.DropDownList }; _lblSystemDetails = new Label { Location = new Point(515, 88), Size = new Size(325, 72), ForeColor = _mutedTextColor }; _btnSaveSelection = CreateButton( "Auswahl übernehmen"); _btnSaveSelection.Location = new Point(640, 153); _btnSaveSelection.Size = new Size(200, 38); _btnSaveSelection.Click += async (_, _) => await SaveSelectionAsync(); _btnAddCredential = CreateButton( "Profil hinzufügen"); _btnAddCredential.Location = new Point(495, 132); _btnAddCredential.Size = new Size(140, 32); _btnAddCredential.Click += async (_, _) => await AddCredentialAsync(); selectionCard.Controls.Add(selectionTitle); selectionCard.Controls.Add(_lblEnvironment); selectionCard.Controls.Add(systemLabel); selectionCard.Controls.Add(_cmbSystem); selectionCard.Controls.Add(credentialLabel); selectionCard.Controls.Add(credentialLabel); selectionCard.Controls.Add(_cmbCredential); selectionCard.Controls.Add(_btnAddCredential); selectionCard.Controls.Add(_lblSystemDetails); selectionCard.Controls.Add(_btnSaveSelection); _btnRefresh = CreateButton( "Erneut prüfen"); _btnRefresh.Location = new Point(525, 580); _btnRefresh.Size = new Size(140, 42); _btnRefresh.Click += async (_, _) => await InitializeAsync(); Button cancelButton = CreateButton( "Abbrechen"); cancelButton.Location = new Point(675, 580); cancelButton.Size = new Size(110, 42); cancelButton.Click += (_, _) => { DialogResult = DialogResult.Cancel; Close(); }; _btnFinish = CreateButton( "Setup abschließen"); _btnFinish.Location = new Point(795, 580); _btnFinish.Size = new Size(140, 42); _btnFinish.Enabled = false; _btnFinish.Click += (_, _) => { DialogResult = DialogResult.OK; Close(); }; Controls.Add(title); Controls.Add(subtitle); Controls.Add(checkCard); Controls.Add(selectionCard); Controls.Add(_btnRefresh); Controls.Add(cancelButton); Controls.Add(_btnFinish); } private async Task InitializeAsync() { if (_running) { return; } SetRunning(true); try { SetupCheckResult result = await _coordinator.CheckAsync(); SetStatus( _lblRuntime, "Runtime", result.RuntimeReady, result.RuntimeMessage); SetStatus( _lblCertificate, "Always Encrypted", result.CertificateReady, result.CertificateMessage); SetStatus( _lblDatabase, "Datenbank", result.DatabaseReady, result.DatabaseMessage); SetStatus( _lblSelection, "Auswahl", result.CredentialsReady, result.CredentialsMessage); _btnImportCertificate.Visible = !result.CertificateReady; _btnFinish.Enabled = result.IsReady; if (result.CertificateReady) { await LoadSystemsAsync(); } } catch (Exception ex) { MessageBox.Show( this, ex.Message, "Setup fehlgeschlagen", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { SetRunning(false); } } private async Task LoadSystemsAsync() { LocalSetupSelection? previousSelection = _coordinator.LoadSelection(); _systems = await _coordinator.LoadSystemsAsync(); _cmbSystem.Items.Clear(); _cmbCredential.Items.Clear(); foreach (SonicSystemOption system in _systems) { _cmbSystem.Items.Add(system); } if (_systems.Count == 0) { _lblSystemDetails.Text = "Kein System für die gewählte Umgebung gefunden."; return; } int selectedIndex = 0; if (previousSelection is not null) { for (int index = 0; index < _systems.Count; index++) { if (_systems[index].SonicConnectionId == previousSelection.SonicConnectionId) { selectedIndex = index; break; } } } _cmbSystem.SelectedIndex = selectedIndex; } private async Task AddCredentialAsync() { if (_running) { return; } if (_cmbSystem.SelectedItem is not SonicSystemOption system) { ShowWarning( "Bitte zuerst ein Sonic-System auswählen."); return; } using AddCredentialProfileForm dialog = new( _coordinator, system); DialogResult dialogResult = dialog.ShowDialog(this); if (dialogResult != DialogResult.OK || dialog.CreatedProfile is null) { return; } SetRunning(true); try { _profiles = await _coordinator.LoadCredentialsAsync( system.SonicConnectionId); _cmbCredential.Items.Clear(); foreach (SonicCredentialProfile profile in _profiles) { _cmbCredential.Items.Add(profile); } for (int index = 0; index < _cmbCredential.Items.Count; index++) { if (_cmbCredential.Items[index] is SonicCredentialProfile profile && profile.SonicCredentialId == dialog.CreatedProfile.SonicCredentialId) { _cmbCredential.SelectedIndex = index; break; } } _lblSystemDetails.Text = $"Domain: {system.DomainName}" + Environment.NewLine + $"Adresse: {system.ConnectionUrl}" + Environment.NewLine + $"Profil: {dialog.CreatedProfile.CredentialName}"; MessageBox.Show( this, "Das geprüfte Benutzerprofil wurde geladen. " + "Klicke jetzt auf „Auswahl übernehmen“.", "Benutzerprofil hinzugefügt", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show( this, ex.Message, "Benutzerprofile konnten nicht neu geladen werden", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { SetRunning(false); } } private async Task SystemChangedAsync() { if (_cmbSystem.SelectedItem is not SonicSystemOption system) { return; } _lblSystemDetails.Text = $"Domain: {system.DomainName}" + Environment.NewLine + $"Adresse: {system.ConnectionUrl}"; _profiles = await _coordinator.LoadCredentialsAsync( system.SonicConnectionId); _cmbCredential.Items.Clear(); foreach (SonicCredentialProfile profile in _profiles) { _cmbCredential.Items.Add(profile); } if (_profiles.Count == 0) { _lblSystemDetails.Text += Environment.NewLine + "Kein Benutzerprofil vorhanden. " + "Bitte ein neues Profil hinzufügen."; _btnSaveSelection.Enabled = false; return; } _btnSaveSelection.Enabled = true; LocalSetupSelection? selection = _coordinator.LoadSelection(); int selectedIndex = 0; if (selection is not null) { for (int index = 0; index < _profiles.Count; index++) { if (_profiles[index].SonicCredentialId == selection.SonicCredentialId) { selectedIndex = index; break; } } } else { int defaultIndex = _profiles .Select( (profile, index) => new { profile, index }) .Where(item => item.profile.IsDefault) .Select(item => item.index) .FirstOrDefault(); selectedIndex = defaultIndex; } _cmbCredential.SelectedIndex = selectedIndex; } private async Task SaveSelectionAsync() { if (_cmbSystem.SelectedItem is not SonicSystemOption system) { ShowWarning( "Bitte ein Sonic-System auswählen."); return; } if (_cmbCredential.SelectedItem is not SonicCredentialProfile profile) { ShowWarning( "Bitte ein Benutzerprofil auswählen."); return; } _coordinator.SaveSelection( system, profile); MessageBox.Show( this, "System und Benutzerprofil wurden ausgewählt.", "Setup", MessageBoxButtons.OK, MessageBoxIcon.Information); await InitializeAsync(); } private async Task ImportCertificateAsync() { using OpenFileDialog dialog = new() { Title = "Always-Encrypted-PFX auswählen", Filter = "PFX-Zertifikat (*.pfx)|*.pfx", CheckFileExists = true, Multiselect = false }; if (dialog.ShowDialog(this) != DialogResult.OK) { return; } string? password = PromptForPassword(); if (password is null) { return; } try { _coordinator.CertificateService.ImportPfx( dialog.FileName, password); password = string.Empty; await InitializeAsync(); } finally { password = string.Empty; } } private static string? PromptForPassword() { using Form form = new() { Text = "PFX-Kennwort", Size = new Size(390, 160), StartPosition = FormStartPosition.CenterParent, FormBorderStyle = FormBorderStyle.FixedDialog }; TextBox textBox = new() { Location = new Point(15, 35), Width = 340, UseSystemPasswordChar = true }; Button ok = new() { Text = "OK", Location = new Point(190, 75), DialogResult = DialogResult.OK }; Button cancel = new() { Text = "Abbrechen", Location = new Point(275, 75), DialogResult = DialogResult.Cancel }; form.Controls.Add(textBox); form.Controls.Add(ok); form.Controls.Add(cancel); form.AcceptButton = ok; form.CancelButton = cancel; return form.ShowDialog() == DialogResult.OK ? textBox.Text : null; } private void SetRunning(bool running) { _running = running; _cmbSystem.Enabled = !running; _cmbCredential.Enabled = !running; _btnAddCredential.Enabled = !running; _btnSaveSelection.Enabled = !running; _btnRefresh.Enabled = !running; _btnImportCertificate.Enabled = !running; Cursor = running ? Cursors.WaitCursor : Cursors.Default; } private void ShowWarning(string message) { MessageBox.Show( this, message, "Setup", MessageBoxButtons.OK, MessageBoxIcon.Warning); } private Panel CreateCard( Point location, Size size) { return new Panel { Location = location, Size = size, BackColor = _cardColor, BorderStyle = BorderStyle.FixedSingle }; } private Label CreateTitle( string text, Point location) { return new Label { Text = text, Location = location, AutoSize = true, ForeColor = _textColor, Font = new Font( "Segoe UI", 14, FontStyle.Bold) }; } private Label CreateLabel( string text, Point location) { return new Label { Text = text, Location = location, AutoSize = true, ForeColor = _mutedTextColor }; } private Label CreateStatus( Point location) { return new Label { Location = location, Size = new Size(620, 32), ForeColor = _mutedTextColor }; } private Button CreateButton(string text) { return new Button { Text = text, BackColor = _goldColor, ForeColor = Color.FromArgb(30, 25, 10), FlatStyle = FlatStyle.Flat, Font = new Font( "Segoe UI", 9, FontStyle.Bold), Cursor = Cursors.Hand }; } private void SetStatus( Label label, string category, bool success, string message) { label.Text = $"{(success ? "✓" : "✗")} " + $"{category}: {message}"; label.ForeColor = success ? _greenColor : _redColor; } }