using Dinah.Core; using Dinah.Core.Logging; using LibationFileManager; using LibationUiBase.Forms; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Serilog; using System; using System.IO; using System.Threading.Tasks; namespace LibationUiBase; /// /// Contains the results of a, initial setup prompt. /// public interface ILibationSetup { public bool IsNewUser { get; } public bool IsReturningUser { get; } } /// /// Contains the results of a Libation Files install location selection prompt. /// public interface ILibationInstallLocation { public string? SelectedDirectory { get; } } /// /// Provides configuration and delegates for running the Libation setup process, including user prompts for initial /// setup and selecting installation locations. /// /// LibationSetup encapsulates the logic required to ensure that Libation is properly configured before /// use. This class is used at application startup to ensure that all required settings are present and valid /// before proceeding. public class LibationSetup { /// Asynchronous delegate to show the setup prompt public Func>? SetupPromptAsync { get; init; } /// Asynchronous delegate to show the Libation Files selection dialog prompt public Func>? SelectFolderPromptAsync { get; init; } /// Synchronous delegate to show the setup prompt public Func? SetupPrompt { get; init; } /// Synchronous delegate to show the Libation Files selection dialog prompt public Func? SelectFolderPrompt { get; init; } private LibationFiles Files { get; } public LibationSetup(LibationFiles libationFiles) { Files = libationFiles; } /// /// Runs Libation setup if needed. /// Verifies that /// /// /// public async Task RunSetupIfNeededAsync() { // all returns should be preceded by either: // - if libationFiles.LibationSettingsAreValid // - OnCancelled() if (Files.SettingsAreValid) return true; // check for existing settings in default location // First check if file exists so that, if it doesn't, we don't // overwrite user's LibationFiles setting in appsettings.json var defaultSettingsFile = Path.Combine(LibationFiles.DefaultLibationFilesDirectory, LibationFiles.SETTINGS_JSON); if (File.Exists(defaultSettingsFile) && LibationFiles.SettingsFileIsValid(defaultSettingsFile)) { Files.SetLibationFiles(LibationFiles.DefaultLibationFilesDirectory); if (Files.SettingsAreValid) return true; } var setupResult = SetupPromptAsync is not null ? await SetupPromptAsync() : SetupPrompt is not null ? SetupPrompt() : throw new InvalidOperationException("No setup prompt provided"); if (setupResult.IsNewUser) { return await CreateDefaultSettingsAsync(); } else if (setupResult.IsReturningUser) { var chooseFolderResult = SelectFolderPromptAsync is not null ? await SelectFolderPromptAsync() : SelectFolderPrompt is not null ? SelectFolderPrompt() : throw new InvalidOperationException("No select folder prompt provided"); if (string.IsNullOrWhiteSpace(chooseFolderResult?.SelectedDirectory)) return false; Files.SetLibationFiles(chooseFolderResult.SelectedDirectory); if (Files.SettingsAreValid) return true; // path did not result in valid settings var continueResult = await MessageBoxBase.ShowAsyncImpl(null, $""" No valid settings were found at this location. Would you like to create a new install settings in this folder? {chooseFolderResult.SelectedDirectory} """, "New install?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); return continueResult == DialogResult.Yes && await CreateDefaultSettingsAsync(); } else { return false; } } private async Task CreateDefaultSettingsAsync() { if (!TryCreateDirectory()) { var result = await MessageBoxBase.ShowAsyncImpl(null, $""" Could not create the Libation Settings folder at: {Files.Location.Path} Would you like to create a new install settings in this folder? {LibationFiles.DefaultLibationFilesDirectory} """, "Error Creating Libation Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (result is not DialogResult.Yes) return false; Files.SetLibationFiles(LibationFiles.DefaultLibationFilesDirectory); //We should never not be able to access DefaultLibationFilesDirectory. //If we can't write here, something is very wrong and we shouldn't even try to continue. if (!TryCreateDirectory()) { await MessageBoxBase.ShowAsyncImpl(null, $""" An error occurred while creating default settings folder: {LibationFiles.DefaultLibationFilesDirectory} """, "Error Creating Libation Settings", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); return false; } } if (Files.SettingsAreValid) return true; try { WriteDefaultSettingsFile(Files.SettingsFilePath); return Files.SettingsAreValid; } catch (Exception ex) { // We are able to create the LibationFiles directory, but we can't write a settings file in it. // Examples of this is the root of a system drive (C:\) Log.Logger.TryLogError(ex, $"Failed to create {LibationFiles.SETTINGS_JSON} in {Files.Location}"); if (!Files.Location.PathWithoutPrefix.Equals(LibationFiles.DefaultLibationFilesDirectory, StringComparison.OrdinalIgnoreCase)) { var result = await MessageBoxBase.ShowAsyncImpl(null, $""" Could not create the Libation Settings file at: {Files.SettingsFilePath} Would you like to create a new install settings in this folder? {LibationFiles.DefaultLibationFilesDirectory} """, "Error Creating Libation Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (result is not DialogResult.Yes) return false; // Try again in the default location Log.Logger.TryLogInformation($"Changing {LibationFiles.LIBATION_FILES_KEY} to {LibationFiles.DefaultLibationFilesDirectory}"); Files.SetLibationFiles(LibationFiles.DefaultLibationFilesDirectory); return await CreateDefaultSettingsAsync(); } else { await MessageBoxBase.ShowAsyncImpl(null, $""" An error occurred while creating default settings file in: {LibationFiles.DefaultLibationFilesDirectory} """, "Error Creating Libation Settings", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); return false; } } } private bool TryCreateDirectory() { try { Directory.CreateDirectory(Files.Location); return true; } catch (Exception ex) { Log.Logger.TryLogError(ex, $"Failed to create {LibationFiles.LIBATION_FILES_KEY} directory at {Files.Location}"); return false; } } private void WriteDefaultSettingsFile(string settingsFilePath) { var booksParent = Configuration.IsWindows ? Files.Location.Path : Configuration.MyMusic; var jObj = new JObject { { nameof(Configuration.Books), Path.Combine(booksParent, nameof(Configuration.Books)) } }; var contents = JsonConvert.SerializeObject(jObj, Formatting.Indented); File.WriteAllText(settingsFilePath, contents); } }