using SQLVision.Core.Enums; using SQLVision.Core.Interfaces; using SQLVision.Core.Models; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Linq; using System.Text; using System.Text.Json; using System.Threading.Tasks; using Windows.ApplicationModel.DataTransfer; namespace SQLVision.UI.ViewModels; public class MainViewModel : ObservableObject { private readonly IScriptManager _scriptManager; private readonly ISqlExecutionService _executionService; private readonly IExportService _exportService; private readonly IControlFactory _controlFactory; private readonly IVisualizerFactory _visualizerFactory; private readonly ILogger _logger; private readonly ObservableCollection _scripts = new(); private readonly ObservableCollection _scriptCategories = new(); private readonly ObservableCollection _history = new(); private readonly ObservableCollection _resultTabs = new(); private ScriptMetadata _selectedScript; private bool _isBusy; private string _statusMessage; private ResultTabViewModel _selectedResultTab; private string _searchText; public ObservableCollection ScriptCategories => _scriptCategories; public ObservableCollection History => _history; public ObservableCollection ResultTabs => _resultTabs; public ScriptMetadata SelectedScript { get => _selectedScript; set { if (SetProperty(ref _selectedScript, value)) { OnSelectedScriptChanged(); } } } public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); } public string StatusMessage { get => _statusMessage; set => SetProperty(ref _statusMessage, value); } public ResultTabViewModel SelectedResultTab { get => _selectedResultTab; set => SetProperty(ref _selectedResultTab, value); } public string SearchText { get => _searchText; set { if (SetProperty(ref _searchText, value)) { FilterScripts(); } } } public ObservableCollection Parameters { get; } = new(); public IAsyncRelayCommand LoadScriptsCommand { get; } public IAsyncRelayCommand ExecuteCommand { get; } public IAsyncRelayCommand ExportCommand { get; } public IRelayCommand CopySqlCommand { get; } public IRelayCommand ClearResultsCommand { get; } public IRelayCommand SaveParametersCommand { get; } public IRelayCommand LoadParametersCommand { get; } public MainViewModel( IScriptManager scriptManager, ISqlExecutionService executionService, IExportService exportService, IControlFactory controlFactory, IVisualizerFactory visualizerFactory, ILogger logger) { _scriptManager = scriptManager; _executionService = executionService; _exportService = exportService; _controlFactory = controlFactory; _visualizerFactory = visualizerFactory; _logger = logger; LoadScriptsCommand = new AsyncRelayCommand(LoadScriptsAsync); ExecuteCommand = new AsyncRelayCommand(ExecuteScriptAsync, CanExecuteScript); ExportCommand = new AsyncRelayCommand(ExportResultsAsync, CanExportResults); CopySqlCommand = new RelayCommand(CopySqlToClipboard); ClearResultsCommand = new RelayCommand(ClearResults); SaveParametersCommand = new RelayCommand(SaveParameters); LoadParametersCommand = new RelayCommand(LoadParameters); // Подписка на события _scriptManager.ScriptChanged += OnScriptChanged; _scriptManager.ScriptsReloaded += OnScriptsReloaded; } private async Task LoadScriptsAsync() { try { IsBusy = true; StatusMessage = "Загрузка скриптов..."; var scripts = await _scriptManager.LoadScriptsAsync(); _scripts.Clear(); foreach (var script in scripts) { _scripts.Add(script); } CategorizeScripts(); StatusMessage = $"Загружено {_scripts.Count} скриптов"; } catch (Exception ex) { _logger.LogError(ex, "Error loading scripts"); StatusMessage = $"Ошибка загрузки: {ex.Message}"; } finally { IsBusy = false; } } private void CategorizeScripts() { _scriptCategories.Clear(); var categories = _scripts .GroupBy(s => s.Category ?? "Без категории") .OrderBy(g => g.Key); foreach (var category in categories) { var scriptCategory = new ScriptCategory { Name = category.Key, Scripts = new ObservableCollection(category.OrderBy(s => s.FileName)) }; _scriptCategories.Add(scriptCategory); } } private void FilterScripts() { if (string.IsNullOrWhiteSpace(SearchText)) { // Показать все скрипты foreach (var category in _scriptCategories) { foreach (var script in category.Scripts) { script.IsVisible = true; } } } else { var searchLower = SearchText.ToLower(); foreach (var category in _scriptCategories) { foreach (var script in category.Scripts) { script.IsVisible = script.FileName.ToLower().Contains(searchLower) || script.Description?.ToLower().Contains(searchLower) == true || script.Tags.Any(t => t.ToLower().Contains(searchLower)); } } } } private void OnSelectedScriptChanged() { Parameters.Clear(); if (_selectedScript == null) return; // Создание ViewModel для каждого параметра foreach (var param in _selectedScript.Parameters.OrderBy(p => p.Order)) { var paramVm = new ParameterViewModel(param, _controlFactory); paramVm.ValueChanged += OnParameterValueChanged; Parameters.Add(paramVm); } // Восстановление сохраненных значений LoadSavedParameters(); } private void OnParameterValueChanged(object sender, EventArgs e) { ExecuteCommand.NotifyCanExecuteChanged(); // Обновление зависимых параметров if (sender is ParameterViewModel changedParam) { UpdateDependentParameters(changedParam); } } private void UpdateDependentParameters(ParameterViewModel changedParam) { foreach (var paramVm in Parameters) { if (paramVm.Parameter.DependsOn == changedParam.Parameter.Name) { paramVm.UpdateDependencies(GetCurrentParameterValues()); } } } private Dictionary GetCurrentParameterValues() { return Parameters.ToDictionary( p => p.Parameter.Name, p => p.Value ?? p.Parameter.DefaultValue); } private bool CanExecuteScript() { if (_selectedScript == null) return false; // Проверка обязательных параметров foreach (var paramVm in Parameters) { if (paramVm.Parameter.IsRequired && (paramVm.Value == null || string.IsNullOrWhiteSpace(paramVm.Value.ToString()))) { return false; } } return true; } private async Task ExecuteScriptAsync() { try { IsBusy = true; StatusMessage = "Выполнение скрипта..."; var parameters = GetCurrentParameterValues(); var result = await _executionService.ExecuteAsync(_selectedScript, parameters); // Добавление в историю var historyItem = new ExecutionHistoryItem { ScriptId = _selectedScript.Id, ScriptName = _selectedScript.FileName, ExecutionTime = DateTime.Now, Duration = result.ExecutionTime, Success = result.IsSuccess, Parameters = new Dictionary(parameters), RowCount = result.RowCount, ErrorMessage = result.ErrorMessage, ExecutedSql = result.ExecutedSql }; _history.Insert(0, historyItem); // Очистка старых записей истории while (_history.Count > 1000) { _history.RemoveAt(_history.Count - 1); } if (result.IsSuccess) { CreateResultTabs(result); StatusMessage = $"Выполнено за {result.ExecutionTime.TotalSeconds:F2} сек. Получено строк: {result.RowCount}"; } else { StatusMessage = $"Ошибка: {result.ErrorMessage}"; ShowErrorDialog(result.ErrorMessage); } // Сохранение параметров SaveParameters(); } catch (Exception ex) { _logger.LogError(ex, "Error executing script"); StatusMessage = $"Ошибка: {ex.Message}"; ShowErrorDialog(ex.Message); } finally { IsBusy = false; } } private void CreateResultTabs(ExecutionResult result) { // Удаляем старые вкладки ResultTabs.Clear(); for (int i = 0; i < result.Data.Tables.Count; i++) { var table = result.Data.Tables[i]; var outputDef = i < _selectedScript.Outputs.Count ? _selectedScript.Outputs[i] : CreateDefaultOutputDefinition(table, i); var visualizer = _visualizerFactory.GetVisualizer(outputDef.Type); var content = visualizer.Visualize(table, outputDef); var tabVm = new ResultTabViewModel { Title = outputDef.Description, Content = content, DataTable = table, OutputDefinition = outputDef, CanExport = true, CanCopy = true }; ResultTabs.Add(tabVm); } if (ResultTabs.Any()) { SelectedResultTab = ResultTabs[0]; } } private OutputDefinition CreateDefaultOutputDefinition(DataTable table, int index) { return new OutputDefinition { Type = OutputType.Table, Description = $"Результат {index + 1}", DataTableName = table.TableName }; } private bool CanExportResults() { return SelectedResultTab != null && SelectedResultTab.DataTable != null && SelectedResultTab.DataTable.Rows.Count > 0; } private async Task ExportResultsAsync() { if (SelectedResultTab?.DataTable == null) return; try { var savePicker = new FileSavePicker(); savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; savePicker.FileTypeChoices.Add("Excel файл", new List { ".xlsx" }); savePicker.FileTypeChoices.Add("CSV файл", new List { ".csv" }); savePicker.FileTypeChoices.Add("JSON файл", new List { ".json" }); savePicker.SuggestedFileName = $"{_selectedScript.FileName}_{DateTime.Now:yyyyMMdd_HHmmss}"; var file = await savePicker.PickSaveFileAsync(); if (file != null) { var options = new ExportOptions { Format = Path.GetExtension(file.Path).TrimStart('.').ToUpper(), IncludeHeaders = true, AutoFilter = true }; await _exportService.ExportAsync(SelectedResultTab.DataTable, file.Path, options); StatusMessage = $"Экспортировано в {file.Path}"; } } catch (Exception ex) { _logger.LogError(ex, "Error exporting results"); StatusMessage = $"Ошибка экспорта: {ex.Message}"; } } private void CopySqlToClipboard() { if (_selectedScript == null) return; var parameters = GetCurrentParameterValues(); var sql = FormatSqlWithParameters(_selectedScript.ProcessedSql, parameters); var package = new DataPackage(); package.SetText(sql); Clipboard.SetContent(package); StatusMessage = "SQL скопирован в буфер обмена"; } private string FormatSqlWithParameters(string sql, Dictionary parameters) { var result = new StringBuilder(sql); foreach (var param in parameters) { var paramName = $"@{param.Key}"; var paramValue = FormatParameterForDisplay(param.Value); result = result.Replace(paramName, paramValue); } return result.ToString(); } private string FormatParameterForDisplay(object value) { if (value == null) return "NULL"; return value switch { string str => $"N'{str.Replace("'", "''")}'", DateTime dt => $"'{dt:yyyy-MM-dd HH:mm:ss}'", bool b => b ? "1" : "0", _ => value.ToString() }; } private void ClearResults() { ResultTabs.Clear(); StatusMessage = "Результаты очищены"; } private void SaveParameters() { if (_selectedScript == null) return; var parameters = GetCurrentParameterValues(); var settings = ApplicationData.Current.LocalSettings; var dict = new Dictionary(); foreach (var param in parameters) { dict[param.Key] = param.Value; } var json = JsonSerializer.Serialize(dict); settings.Values[$"ScriptParams_{_selectedScript.Id}"] = json; StatusMessage = "Параметры сохранены"; } private void LoadParameters() { if (_selectedScript == null) return; var settings = ApplicationData.Current.LocalSettings; if (settings.Values.TryGetValue($"ScriptParams_{_selectedScript.Id}", out var jsonObj)) { try { var savedParams = JsonSerializer.Deserialize>(jsonObj.ToString()); foreach (var paramVm in Parameters) { if (savedParams.TryGetValue(paramVm.Parameter.Name, out var value)) { paramVm.Value = value; } } StatusMessage = "Параметры загружены"; } catch (Exception ex) { _logger.LogWarning(ex, "Error loading saved parameters"); } } } private void ShowErrorDialog(string message) { // Реализация диалога ошибки // Можно использовать ContentDialog или другое UI решение } private void OnScriptChanged(object sender, ScriptChangedEventArgs e) { // Обновление UI при изменении скрипта DispatcherQueue.GetForCurrentThread().TryEnqueue(() => { CategorizeScripts(); if (e.ChangeType == ScriptChangeType.Deleted && _selectedScript?.FullPath == e.FilePath) { SelectedScript = null; } }); } private void OnScriptsReloaded(object sender, ScriptsReloadedEventArgs e) { DispatcherQueue.GetForCurrentThread().TryEnqueue(CategorizeScripts); } }