using System.Collections.ObjectModel; using System.Text.RegularExpressions; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using OF_DL.Models.Config; using Serilog; namespace OF_DL.Gui.ViewModels; public partial class CreatorConfigModalViewModel : ViewModelBase { private static readonly Regex s_fileNameVariableRegex = new(@"\{([^{}]+)\}", RegexOptions.Compiled); private static readonly string[] s_postFileNameVariables = ["id", "postedAt", "mediaId", "mediaCreatedAt", "filename", "username", "text"]; private static readonly string[] s_messageFileNameVariables = ["id", "createdAt", "mediaId", "mediaCreatedAt", "filename", "username", "text"]; private readonly Action _onClose; private readonly Func _isUsernameDuplicate; [ObservableProperty] private bool _isOpen; [ObservableProperty] private bool _isEditMode; [ObservableProperty] private string _originalUsername = string.Empty; [ObservableProperty] private string _username = string.Empty; [ObservableProperty] private string _usernameError = string.Empty; [ObservableProperty] private string _paidPostFileNameFormat = string.Empty; [ObservableProperty] private string _postFileNameFormat = string.Empty; [ObservableProperty] private string _paidMessageFileNameFormat = string.Empty; [ObservableProperty] private string _messageFileNameFormat = string.Empty; [ObservableProperty] private string _selectedPaidPostVariable = string.Empty; [ObservableProperty] private string _selectedPostVariable = string.Empty; [ObservableProperty] private string _selectedPaidMessageVariable = string.Empty; [ObservableProperty] private string _selectedMessageVariable = string.Empty; [ObservableProperty] private string _unknownPaidPostVariablesMessage = string.Empty; [ObservableProperty] private string _unknownPostVariablesMessage = string.Empty; [ObservableProperty] private string _unknownPaidMessageVariablesMessage = string.Empty; [ObservableProperty] private string _unknownMessageVariablesMessage = string.Empty; public ObservableCollection AvailableUsers { get; } public ObservableCollection PaidPostVariables { get; } = []; public ObservableCollection PostVariables { get; } = []; public ObservableCollection PaidMessageVariables { get; } = []; public ObservableCollection MessageVariables { get; } = []; public ObservableCollection PaidPostSegments { get; } = []; public ObservableCollection PostSegments { get; } = []; public ObservableCollection PaidMessageSegments { get; } = []; public ObservableCollection MessageSegments { get; } = []; public bool HasUsernameError => !string.IsNullOrWhiteSpace(UsernameError); public bool HasUnknownPaidPostVariables => !string.IsNullOrWhiteSpace(UnknownPaidPostVariablesMessage); public bool HasUnknownPostVariables => !string.IsNullOrWhiteSpace(UnknownPostVariablesMessage); public bool HasUnknownPaidMessageVariables => !string.IsNullOrWhiteSpace(UnknownPaidMessageVariablesMessage); public bool HasUnknownMessageVariables => !string.IsNullOrWhiteSpace(UnknownMessageVariablesMessage); public string DialogTitle => IsEditMode ? "Edit Creator Config" : "Add Creator Config"; public CreatorConfigModalViewModel(IEnumerable availableUsers, Action onClose, Func isUsernameDuplicate) { AvailableUsers = new ObservableCollection(availableUsers); _onClose = onClose; _isUsernameDuplicate = isUsernameDuplicate; foreach (string variable in s_postFileNameVariables) { PaidPostVariables.Add(variable); PostVariables.Add(variable); } foreach (string variable in s_messageFileNameVariables) { PaidMessageVariables.Add(variable); MessageVariables.Add(variable); } SelectedPaidPostVariable = PaidPostVariables.FirstOrDefault() ?? string.Empty; SelectedPostVariable = PostVariables.FirstOrDefault() ?? string.Empty; SelectedPaidMessageVariable = PaidMessageVariables.FirstOrDefault() ?? string.Empty; SelectedMessageVariable = MessageVariables.FirstOrDefault() ?? string.Empty; } public void OpenForAdd() { Log.Information("=== OpenForAdd called ==="); IsEditMode = false; OriginalUsername = string.Empty; Username = string.Empty; UsernameError = string.Empty; PaidPostFileNameFormat = string.Empty; PostFileNameFormat = string.Empty; PaidMessageFileNameFormat = string.Empty; MessageFileNameFormat = string.Empty; ClearAllPreviews(); Log.Information("About to set IsOpen = true"); IsOpen = true; Log.Information("=== OpenForAdd: IsOpen is now {IsOpen} ===", IsOpen); } public void OpenForEdit(string username, CreatorConfig config) { IsEditMode = true; OriginalUsername = username; Username = username; UsernameError = string.Empty; PaidPostFileNameFormat = config.PaidPostFileNameFormat ?? string.Empty; PostFileNameFormat = config.PostFileNameFormat ?? string.Empty; PaidMessageFileNameFormat = config.PaidMessageFileNameFormat ?? string.Empty; MessageFileNameFormat = config.MessageFileNameFormat ?? string.Empty; UpdateAllPreviews(); IsOpen = true; } public (string Username, CreatorConfig Config)? GetResult() { if (!Validate()) { return null; } CreatorConfig config = new() { PaidPostFileNameFormat = string.IsNullOrWhiteSpace(PaidPostFileNameFormat) ? null : PaidPostFileNameFormat, PostFileNameFormat = string.IsNullOrWhiteSpace(PostFileNameFormat) ? null : PostFileNameFormat, PaidMessageFileNameFormat = string.IsNullOrWhiteSpace(PaidMessageFileNameFormat) ? null : PaidMessageFileNameFormat, MessageFileNameFormat = string.IsNullOrWhiteSpace(MessageFileNameFormat) ? null : MessageFileNameFormat }; return (Username.Trim(), config); } [RelayCommand] private void InsertPaidPostVariable() { if (!string.IsNullOrWhiteSpace(SelectedPaidPostVariable)) { PaidPostFileNameFormat += $"{{{SelectedPaidPostVariable}}}"; } } [RelayCommand] private void InsertPostVariable() { if (!string.IsNullOrWhiteSpace(SelectedPostVariable)) { PostFileNameFormat += $"{{{SelectedPostVariable}}}"; } } [RelayCommand] private void InsertPaidMessageVariable() { if (!string.IsNullOrWhiteSpace(SelectedPaidMessageVariable)) { PaidMessageFileNameFormat += $"{{{SelectedPaidMessageVariable}}}"; } } [RelayCommand] private void InsertMessageVariable() { if (!string.IsNullOrWhiteSpace(SelectedMessageVariable)) { MessageFileNameFormat += $"{{{SelectedMessageVariable}}}"; } } [RelayCommand] private void Confirm() { if (Validate()) { _onClose(true); } } [RelayCommand] private void Cancel() { _onClose(false); } partial void OnIsOpenChanged(bool value) { Log.Information("*** IsOpen property changed to: {Value} ***", value); } partial void OnPaidPostFileNameFormatChanged(string value) => UpdatePaidPostPreview(); partial void OnPostFileNameFormatChanged(string value) => UpdatePostPreview(); partial void OnPaidMessageFileNameFormatChanged(string value) => UpdatePaidMessagePreview(); partial void OnMessageFileNameFormatChanged(string value) => UpdateMessagePreview(); private bool Validate() { UsernameError = string.Empty; if (string.IsNullOrWhiteSpace(Username)) { UsernameError = "Username is required."; return false; } string trimmedUsername = Username.Trim(); if (!IsEditMode || trimmedUsername != OriginalUsername) { if (_isUsernameDuplicate()) { UsernameError = "A config for this username already exists."; return false; } } return true; } private void ClearAllPreviews() { PaidPostSegments.Clear(); PostSegments.Clear(); PaidMessageSegments.Clear(); MessageSegments.Clear(); UnknownPaidPostVariablesMessage = string.Empty; UnknownPostVariablesMessage = string.Empty; UnknownPaidMessageVariablesMessage = string.Empty; UnknownMessageVariablesMessage = string.Empty; } private void UpdateAllPreviews() { UpdatePaidPostPreview(); UpdatePostPreview(); UpdatePaidMessagePreview(); UpdateMessagePreview(); } private void UpdatePaidPostPreview() => UpdateFileNamePreview(PaidPostFileNameFormat, PaidPostSegments, s_postFileNameVariables, msg => UnknownPaidPostVariablesMessage = msg); private void UpdatePostPreview() => UpdateFileNamePreview(PostFileNameFormat, PostSegments, s_postFileNameVariables, msg => UnknownPostVariablesMessage = msg); private void UpdatePaidMessagePreview() => UpdateFileNamePreview(PaidMessageFileNameFormat, PaidMessageSegments, s_messageFileNameVariables, msg => UnknownPaidMessageVariablesMessage = msg); private void UpdateMessagePreview() => UpdateFileNamePreview(MessageFileNameFormat, MessageSegments, s_messageFileNameVariables, msg => UnknownMessageVariablesMessage = msg); private void UpdateFileNamePreview(string format, ObservableCollection segments, string[] allowedVariables, Action setUnknownMessage) { segments.Clear(); setUnknownMessage(string.Empty); if (string.IsNullOrEmpty(format)) { return; } HashSet allowedSet = new(allowedVariables, StringComparer.OrdinalIgnoreCase); HashSet unknownVariables = new(StringComparer.OrdinalIgnoreCase); MatchCollection matches = s_fileNameVariableRegex.Matches(format); int currentIndex = 0; foreach (Match match in matches) { if (match.Index > currentIndex) { string plainText = format[currentIndex..match.Index]; segments.Add(new FileNameFormatSegmentViewModel(plainText, "#1F2A44")); } string variableName = match.Groups[1].Value; bool isAllowed = allowedSet.Contains(variableName); segments.Add(new FileNameFormatSegmentViewModel(match.Value, isAllowed ? "#2E6EEA" : "#D84E4E")); if (!isAllowed) { unknownVariables.Add(variableName); } currentIndex = match.Index + match.Length; } if (currentIndex < format.Length) { string trailingText = format[currentIndex..]; segments.Add(new FileNameFormatSegmentViewModel(trailingText, "#1F2A44")); } if (unknownVariables.Count > 0) { string tokens = string.Join(", ", unknownVariables.Select(v => $"{{{v}}}")); setUnknownMessage($"Unknown variable(s): {tokens}"); } } }