Декомпозируй этот код используя SOLID и MVVM: [using OfficeOpenXml;
using ParsForAlwaysRegularBigSalaryPls.Commands;
using ParsForAlwaysRegularBigSalaryPls.Extentions;
using ParsForAlwaysRegularBigSalaryPls.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace ParsForAlwaysRegularBigSalaryPls.ViewModels
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private string _inputFilePath;
        private string _outputFilePath;
        private ObservableCollection<ExcelData> _excelData;

        public string InputFilePath
        {
            get => _inputFilePath;
            set => SetProperty(ref _inputFilePath, value);
        }

        public string OutputFilePath
        {
            get => _outputFilePath;
            set => SetProperty(ref _outputFilePath, value);
        }

        ObservableCollection<ExcelData> ExcelData
        {
            get => _excelData;
            set => SetProperty(ref _excelData, value);
        }

        public ICommand ProcessCommand { get; }

        public MainViewModel()
        {
            ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
            ProcessCommand = new RelayCommand(ProcessExcelFile);
            ExcelData = new ObservableCollection<ExcelData>();
        }

        private void ProcessExcelFile()
        {
            if (!File.Exists(InputFilePath) || string.IsNullOrWhiteSpace(OutputFilePath))
                return;

            FileInfo inputFile = new FileInfo(InputFilePath);
            FileInfo outputFile = new FileInfo(OutputFilePath);

            ExcelPackage package = null;
            try
            {
                package = new ExcelPackage(inputFile);
                var worksheet = package.Workbook.Worksheets[0];
                int rowCount = worksheet.Dimension.Rows;

                // Начинаем с 7-й строки, далее по всем строкам
                for (int row = 7; row <= rowCount; row++)
                {
                    double realKtu = worksheet.Cells[row, 22].GetValue<double>();
                    double currentKtu = worksheet.Cells[row, 21].GetValue<double>();
                    double difference = realKtu - currentKtu;

                    if (Math.Abs(difference) > 1e-9)
                    {
                        AdjustKValues(worksheet, row, difference);
                    }
                }

                package.SaveAs(outputFile);
            }
            finally
            {
                package?.Dispose();
            }
        }

        /// <summary>
        /// Корректирует значения в жёлтых ячейках так, чтобы сумма увеличилась/уменьшилась на difference, 
        /// не выходя за пределы min/max, и создавая "живое" распределение.
        /// </summary>
        private void AdjustKValues(ExcelWorksheet worksheet, int row, double difference)
        {
            var (yellowColumns, originalValues, minValues, maxValues) = GetColumnValues(worksheet, row);
            if (!yellowColumns.Any()) return;

            double originalYellowSum = yellowColumns.Sum(idx => originalValues[idx]);
            double targetSum = originalYellowSum + difference;

            double sumOfMins = yellowColumns.Sum(idx => minValues[idx]);
            double sumOfMaxs = yellowColumns.Sum(idx => maxValues[idx]);

            // Проверяем возможность достижения целевой суммы
            if (targetSum < sumOfMins - 1e-9 || targetSum > sumOfMaxs + 1e-9)
            {
                return;
            }

            // Копируем оригинальные значения
            double[] newValues = new double[originalValues.Length];
            Array.Copy(originalValues, newValues, originalValues.Length);

            Random rnd = new Random();

            // 1. Генерируем начальные значения для столбцов
            foreach (var idx in yellowColumns)
            {
                double colMin = minValues[idx];
                double colMax = maxValues[idx];
                newValues[idx] = GenerateRandomValue(rnd, colMin, colMax);
            }

            // 2. Подгоняем сумму значений к целевой (targetSum)
            AdjustValuesToMatchSum(rnd, yellowColumns, newValues, minValues, maxValues, targetSum);

            // 3. Сглаживаем значения для первого столбца
            BalanceFirstColumn(rnd, yellowColumns, newValues, minValues, maxValues);

            // 4. Вносим небольшие случайные корректировки для реалистичности
            IntroduceGranularAdjustments(rnd, yellowColumns, newValues, minValues, maxValues);

            // Записываем рассчитанные значения обратно в таблицу
            ApplyNewValues(worksheet, row, yellowColumns, newValues);
            UpdateKtuValues(worksheet, row, difference);
        }

        /// Генерация случайного значения внутри диапазона с правдоподобностью (нормальное распределение)
        private double GenerateRandomValue(Random rnd, double min, double max)
        {
            double mean = (min + max) / 2.0;
            double sigma = (max - min) / 6.0; // 99.7% значений попадут в диапазон [min; max]
            double value;

            // Генерируем значение до тех пор, пока оно не попадет в диапазон [min; max]
            do
            {
                double u1 = 1.0 - rnd.NextDouble();
                double u2 = 1.0 - rnd.NextDouble();
                double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
                value = mean + sigma * randStdNormal;

                // Если слишком много попыток, генерируем равномерное значение.
                if (rnd.Next(1000) == 0)
                {
                    value = min + rnd.NextDouble() * (max - min);
                    break;
                }
            } while (value < min || value > max);

            return value;
        }

        /// Корректировка значений так, чтобы их сумма совпадала с целевой
        private void AdjustValuesToMatchSum(Random rnd, List<int> yellowColumns, double[] newValues, double[] minValues, double[] maxValues, double targetSum)
        {
            double currentSum = yellowColumns.Sum(idx => newValues[idx]);
            double remaining = targetSum - currentSum;

            int iterations = 300; // Максимальное количество попыток исправить сумму
            for (int iter = 0; iter < iterations && Math.Abs(remaining) > 1e-9; iter++)
            {
                foreach (var colIdx in yellowColumns.OrderBy(_ => rnd.Next())) // Рандомизируем порядок
                {
                    double currentVal = newValues[colIdx];
                    double colMin = minValues[colIdx];
                    double colMax = maxValues[colIdx];

                    double delta = 0;
                    if (remaining > 0)
                    {
                        double canIncrease = colMax - currentVal;
                        delta = Math.Min(canIncrease, remaining) * rnd.NextDouble();
                    }
                    else
                    {
                        double canDecrease = currentVal - colMin;
                        delta = -Math.Min(canDecrease, -remaining) * rnd.NextDouble();
                    }

                    currentVal += delta;
                    newValues[colIdx] = currentVal;
                    remaining -= delta;

                    if (Math.Abs(remaining) <= 1e-9) break;
                }
            }
        }

        /// Балансировка первого столбца относительно соседей
        private void BalanceFirstColumn(Random rnd, List<int> yellowColumns, double[] newValues, double[] minValues, double[] maxValues)
        {
            if (yellowColumns.Count < 2) return;

            int firstCol = yellowColumns[0];
            double firstVal = newValues[firstCol];

            // Рассчитаем среднее значение для соседних столбцов
            double neighborAvg = yellowColumns.Skip(1).Average(idx => newValues[idx]);

            // Если первый столбец слишком высок, уменьшить его
            if (firstVal > neighborAvg * 0.1) // Например, на 10% больше
            {
                double maxDecrease = firstVal - minValues[firstCol];
                double adjustment = Math.Min(maxDecrease, (firstVal - neighborAvg) * rnd.NextDouble());
                newValues[firstCol] -= adjustment;
            }
            // Если первый столбец слишком низок, увеличить его
            else if (firstVal < neighborAvg * 0.9) // На 10% меньше
            {
                double maxIncrease = maxValues[firstCol] - firstVal;
                double adjustment = Math.Min(maxIncrease, (neighborAvg - firstVal) * rnd.NextDouble());
                newValues[firstCol] += adjustment;
            }

            // Финальная корректировка для равновесия
            newValues[firstCol] = Clamp(newValues[firstCol], minValues[firstCol], maxValues[firstCol]);
        }

        private static double Clamp(double value, double min, double max)
        {
            if (value < min) return min;
            if (value > max) return max;
            return value;
        }
        /// Вносим небольшие случайные корректировки для реалистичности
        private void IntroduceGranularAdjustments(Random rnd, List<int> yellowColumns, double[] newValues, double[] minValues, double[] maxValues)
        {
            for (int i = 0; i < 100; i++)
            {
                if (yellowColumns.Count < 2) break;

                // Берем две случайные колонки
                int colIdx1 = yellowColumns[rnd.Next(yellowColumns.Count)];
                int colIdx2 = yellowColumns[rnd.Next(yellowColumns.Count)];

                if (colIdx1 == colIdx2) continue;

                double v1 = newValues[colIdx1];
                double v2 = newValues[colIdx2];
                double min1 = minValues[colIdx1], max1 = maxValues[colIdx1];
                double min2 = minValues[colIdx2], max2 = maxValues[colIdx2];

                // Вносим небольшую корректировку от одной колонки к другой
                double adjustment = (rnd.NextDouble() - 0.5) * (max1 - min1 + max2 - min2) * 0.01;

                if (v1 - adjustment >= min1 && v1 - adjustment <= max1 &&
                    v2 + adjustment >= min2 && v2 + adjustment <= max2)
                {
                    newValues[colIdx1] -= adjustment;
                    newValues[colIdx2] += adjustment;
                }
            }
        }

        // Метод для генерации нормального распределения (Box-Muller)
        private double NormalRandom(Random rnd, double mean, double sigma)
        {
            double u1 = 1.0 - rnd.NextDouble();
            double u2 = 1.0 - rnd.NextDouble();
            double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
            return mean + sigma * randStdNormal;
        }

        // Метод перетасовки
        private void Shuffle<T>(IList<T> list, Random rnd)
        {
            for (int i = list.Count - 1; i > 0; i--)
            {
                int k = rnd.Next(i + 1);
                (list[i], list[k]) = (list[k], list[i]);
            }
        }

        /// <summary>
        /// Извлекает информацию о желтых столбцах, их исходных, минимальных и максимальных значениях.
        /// </summary>
        private (List<int> yellowColumns, double[] originalValues, double[] minValues, double[] maxValues) GetColumnValues(ExcelWorksheet worksheet, int row)
        {
            const int startCol = 5, endCol = 15;
            const int rowMinValues = 1, rowMaxValues = 2;

            var yellowColumns = new List<int>();
            var originalValues = new double[endCol - startCol + 1];
            var minValues = new double[endCol - startCol + 1];
            var maxValues = new double[endCol - startCol + 1];

            for (int col = startCol; col <= endCol; col++)
            {
                minValues[col - startCol] = worksheet.Cells[rowMinValues, col].GetValue<double>();
                maxValues[col - startCol] = worksheet.Cells[rowMaxValues, col].GetValue<double>();

                var cell = worksheet.Cells[row, col];
                var backgroundColor = cell.Style.Fill.BackgroundColor.LookupColor();
                originalValues[col - startCol] = cell.GetValue<double>();

                // Предполагается, что жёлтый цвет определяется точно так:
                if (backgroundColor == "#FFFFFF00") // Желтый
                {
                    yellowColumns.Add(col - startCol);
                }
            }

            return (yellowColumns, originalValues, minValues, maxValues);
        }

        /// <summary>
        /// Применяет новые значения к желтым ячейкам.
        /// </summary>
        private void ApplyNewValues(ExcelWorksheet worksheet, int row, List<int> yellowColumns, double[] newValues)
        {
            int startCol = 5;
            foreach (var idx in yellowColumns)
            {
                worksheet.Cells[row, startCol + idx].Value = Math.Round(newValues[idx], 6);
            }
        }

        /// <summary>
        /// Обновляет значения КТУ (столбцы 21 и 23).
        /// Предполагается, что разница должна обнулиться в 23-м столбце.
        /// </summary>
        private void UpdateKtuValues(ExcelWorksheet worksheet, int row, double difference)
        {
            worksheet.Cells[row, 21].Value = worksheet.Cells[row, 21].GetValue<double>() + difference;
            worksheet.Cells[row, 23].Value = 0;
        }

        private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (!Equals(field, value))
            {
                field = value;
                OnPropertyChanged(propertyName);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
]