# 📚 History System - Система истории изменений

CSElement включает мощную систему истории изменений с поддержкой undo/redo операций, создания снимков состояния и отслеживания всех изменений в данных.

## 📋 Содержание

- [Основы History System](#основы-history-system)
- [Конфигурация и настройка](#конфигурация-и-настройка)
- [Операции и снимки](#операции-и-снимки)
- [Undo/Redo функциональность](#undoredo-функциональность)
- [События и мониторинг](#события-и-мониторинг)
- [Экспорт и импорт истории](#экспорт-и-импорт-истории)
- [Полный пример](#полный-пример-редактор-документов)

## Основы History System

### Создание и инициализация

```typescript
import { 
  HistoryManagerImpl, 
  HistoryConfig,
  HistoryOperation 
} from 'cs-element';

// Базовая инициализация
const historyManager = new HistoryManagerImpl();

// Расширенная конфигурация
const config: HistoryConfig = {
  maxOperations: 200,        // Максимум 200 операций в истории
  maxSize: 50 * 1024 * 1024, // Максимум 50MB
  snapshotInterval: 15,      // Снимок каждые 15 операций
  autoCleanup: true,         // Автоматическая очистка
  compression: true,         // Сжатие снимков
  trackOperations: ['update', 'delete'], // Отслеживать только эти операции
  ignoreOperations: ['temp'] // Игнорировать временные операции
};

const historyManager = new HistoryManagerImpl(config);
```

### Интеграция с CSElement

```typescript
// Автоматическое отслеживание изменений
class HistoryAwareElement extends CSElement {
  private historyManager: HistoryManagerImpl;

  constructor(name: string, historyManager: HistoryManagerImpl) {
    super(name);
    this.historyManager = historyManager;
    
    // Подписываемся на события изменения данных
    this.on('data:set', (key, newValue, oldValue) => {
      this.historyManager.addOperation({
        type: 'update',
        description: `Updated ${key} in ${this.name}`,
        before: { [key]: oldValue },
        after: { [key]: newValue },
        path: [this.id, key],
        canUndo: true,
        canRedo: true
      });
    });
  }

  async setDataWithHistory(key: string, value: any): Promise<void> {
    const oldValue = this.getData(key);
    await this.setData(key, value);
    
    // История автоматически записывается через событие
  }
}
```

## Конфигурация и настройка

### Параметры конфигурации

```typescript
interface HistoryConfig {
  // Ограничения размера
  maxOperations?: number;     // Максимальное количество операций
  maxSize?: number;          // Максимальный размер в байтах
  
  // Снимки состояния
  snapshotInterval?: number; // Интервал создания снимков
  compression?: boolean;     // Сжатие данных снимков
  
  // Управление операциями
  trackOperations?: string[];   // Отслеживать только эти типы
  ignoreOperations?: string[];  // Игнорировать эти типы
  
  // Оптимизация
  autoCleanup?: boolean;     // Автоматическая очистка
}

// Пример специализированной конфигурации
const documentEditorConfig: HistoryConfig = {
  maxOperations: 500,
  snapshotInterval: 20,
  compression: true,
  trackOperations: ['update', 'delete', 'create'],
  ignoreOperations: ['cursor-move', 'scroll', 'focus'],
  autoCleanup: true
};
```

### Динамическая настройка

```typescript
class ConfigurableHistoryManager extends HistoryManagerImpl {
  updateConfig(newConfig: Partial<HistoryConfig>): void {
    // Обновляем конфигурацию на лету
    Object.assign(this.config, newConfig);
    
    // Применяем новые ограничения
    if (newConfig.maxOperations && 
        this.operations.length > newConfig.maxOperations) {
      this.cleanup();
    }
  }

  enableVerboseLogging(): void {
    this.updateConfig({
      trackOperations: [], // Отслеживать все операции
      ignoreOperations: []
    });
  }

  enableMinimalTracking(): void {
    this.updateConfig({
      trackOperations: ['update', 'delete'],
      ignoreOperations: ['temp', 'cache', 'ui-state']
    });
  }
}
```

## Операции и снимки

### Ручное добавление операций

```typescript
// Простая операция
historyManager.addOperation({
  type: 'update',
  description: 'Changed user name',
  before: { name: 'John' },
  after: { name: 'Jane' },
  canUndo: true,
  canRedo: true
});

// Операция с метаданными
historyManager.addOperation({
  type: 'delete',
  description: 'Deleted comment',
  before: { 
    id: 'comment-123',
    text: 'This is a comment',
    author: 'user-456'
  },
  after: null,
  path: ['comments', 'comment-123'],
  canUndo: true,
  canRedo: false, // Нельзя повторить удаление
  metadata: {
    author: 'user-789',
    reason: 'inappropriate-content',
    timestamp: Date.now()
  }
});

// Batch операция
historyManager.addOperation({
  type: 'batch',
  description: 'Bulk update users',
  before: [
    { id: 'user-1', status: 'inactive' },
    { id: 'user-2', status: 'inactive' }
  ],
  after: [
    { id: 'user-1', status: 'active' },
    { id: 'user-2', status: 'active' }
  ],
  canUndo: true,
  canRedo: true,
  metadata: {
    batchSize: 2,
    operation: 'activate-users'
  }
});
```

### Создание снимков

```typescript
// Автоматические снимки создаются каждые N операций
// Ручное создание снимка
const element = new CSElement('document');
await element.setData('title', 'My Document');
await element.setData('content', 'Document content...');

const snapshot = historyManager.createSnapshot(
  element.export(),
  'Document saved checkpoint'
);

console.log('Снимок создан:', {
  id: snapshot.id,
  size: snapshot.size,
  timestamp: new Date(snapshot.timestamp)
});

// Снимок с дополнительными метаданными
const detailedSnapshot = historyManager.createSnapshot(
  {
    elements: [element.export()],
    metadata: {
      version: '1.2.0',
      user: 'user-123',
      session: 'session-456'
    }
  },
  'Version 1.2.0 release'
);
```

### Получение информации об операциях

```typescript
// Получить последние 10 операций
const recentOperations = historyManager.getOperations(10);
console.log('Последние операции:', recentOperations);

// Получить конкретную операцию
const operation = historyManager.getOperation('operation-id-123');
if (operation) {
  console.log('Операция найдена:', operation.description);
}

// Получить все снимки
const snapshots = historyManager.getSnapshots();
console.log(`Всего снимков: ${snapshots.length}`);

// Получить состояние истории
const state = historyManager.getState();
console.log('Состояние истории:', {
  canUndo: state.canUndo,
  canRedo: state.canRedo,
  currentIndex: state.currentIndex,
  totalOperations: state.totalOperations,
  totalSize: `${(state.totalSize / 1024 / 1024).toFixed(2)} MB`
});
```

## Undo/Redo функциональность

### Базовые операции отмены и повтора

```typescript
// Проверяем возможность отмены/повтора
const state = historyManager.getState();
console.log(`Можно отменить: ${state.canUndo}`);
console.log(`Можно повторить: ${state.canRedo}`);

// Отмена последней операции
if (state.canUndo) {
  try {
    const restoredData = await historyManager.undo();
    console.log('Операция отменена, восстановлены данные:', restoredData);
  } catch (error) {
    console.error('Ошибка отмены:', error);
  }
}

// Повтор отмененной операции
if (historyManager.getState().canRedo) {
  try {
    const redoneData = await historyManager.redo();
    console.log('Операция повторена:', redoneData);
  } catch (error) {
    console.error('Ошибка повтора:', error);
  }
}
```

### Отмена/повтор до конкретной операции

```typescript
// Получаем список операций для выбора
const operations = historyManager.getOperations();
console.log('Доступные операции:');
operations.forEach((op, index) => {
  console.log(`${index}: ${op.description} (${new Date(op.timestamp)})`);
});

// Отменяем до конкретной операции
const targetOperationId = operations[5].id;
try {
  const result = await historyManager.undoTo(targetOperationId);
  console.log('Отмена до операции выполнена:', result);
} catch (error) {
  console.error('Ошибка отмены до операции:', error);
}

// Повторяем до конкретной операции
const redoTargetId = operations[8].id;
try {
  const result = await historyManager.redoTo(redoTargetId);
  console.log('Повтор до операции выполнен:', result);
} catch (error) {
  console.error('Ошибка повтора до операции:', error);
}
```

### Интеграция с UI

```typescript
class HistoryUI {
  private historyManager: HistoryManagerImpl;
  private undoButton: HTMLButtonElement;
  private redoButton: HTMLButtonElement;
  private historyList: HTMLElement;

  constructor(historyManager: HistoryManagerImpl) {
    this.historyManager = historyManager;
    this.initializeUI();
    this.bindEvents();
  }

  private initializeUI(): void {
    // Создаем UI элементы
    this.undoButton = document.createElement('button');
    this.undoButton.textContent = 'Undo';
    
    this.redoButton = document.createElement('button');
    this.redoButton.textContent = 'Redo';
    
    this.historyList = document.createElement('ul');
    this.historyList.className = 'history-list';
    
    this.updateUI();
  }

  private bindEvents(): void {
    // Обработчики кнопок
    this.undoButton.addEventListener('click', async () => {
      try {
        await this.historyManager.undo();
        this.updateUI();
      } catch (error) {
        console.error('Undo failed:', error);
      }
    });

    this.redoButton.addEventListener('click', async () => {
      try {
        await this.historyManager.redo();
        this.updateUI();
      } catch (error) {
        console.error('Redo failed:', error);
      }
    });

    // Подписка на события истории
    this.historyManager.on('operation-added', () => this.updateUI());
    this.historyManager.on('undo-performed', () => this.updateUI());
    this.historyManager.on('redo-performed', () => this.updateUI());
  }

  private updateUI(): void {
    const state = this.historyManager.getState();
    
    // Обновляем состояние кнопок
    this.undoButton.disabled = !state.canUndo;
    this.redoButton.disabled = !state.canRedo;
    
    // Обновляем список операций
    this.updateHistoryList();
  }

  private updateHistoryList(): void {
    const operations = this.historyManager.getOperations(20);
    const state = this.historyManager.getState();
    
    this.historyList.innerHTML = '';
    
    operations.forEach((operation, index) => {
      const li = document.createElement('li');
      li.textContent = operation.description;
      li.className = index <= state.currentIndex ? 'applied' : 'unapplied';
      
      // Клик для отмены/повтора до этой операции
      li.addEventListener('click', async () => {
        try {
          if (index < state.currentIndex) {
            await this.historyManager.undoTo(operation.id);
          } else if (index > state.currentIndex) {
            await this.historyManager.redoTo(operation.id);
          }
          this.updateUI();
        } catch (error) {
          console.error('Navigation failed:', error);
        }
      });
      
      this.historyList.appendChild(li);
    });
  }
}
```

## События и мониторинг

### Подписка на события истории

```typescript
// Подписка на все типы событий
historyManager.on('operation-added', (data) => {
  console.log('Новая операция:', data.operation.description);
  console.log('Состояние истории:', data.state);
});

historyManager.on('snapshot-created', (data) => {
  console.log('Создан снимок:', data.snapshot.metadata.operation);
  console.log('Размер снимка:', data.snapshot.size, 'байт');
});

historyManager.on('undo-performed', (data) => {
  console.log('Выполнена отмена:', data.operation.description);
  console.log('Восстановлено состояние:', data.operation.before);
});

historyManager.on('redo-performed', (data) => {
  console.log('Выполнен повтор:', data.operation.description);
  console.log('Применено состояние:', data.operation.after);
});

historyManager.on('history-cleared', (data) => {
  console.log('История очищена в:', new Date(data.timestamp));
});

historyManager.on('cleanup-performed', (data) => {
  console.log('Выполнена очистка истории');
  console.log('Новое состояние:', data.state);
});
```

### Мониторинг производительности

```typescript
class HistoryMonitor {
  private historyManager: HistoryManagerImpl;
  private metrics = {
    operationsPerSecond: 0,
    averageOperationSize: 0,
    memoryUsage: 0,
    undoRedoRatio: 0
  };

  constructor(historyManager: HistoryManagerImpl) {
    this.historyManager = historyManager;
    this.startMonitoring();
  }

  private startMonitoring(): void {
    // Мониторинг каждые 5 секунд
    setInterval(() => {
      this.updateMetrics();
      this.logMetrics();
    }, 5000);

    // Отслеживание операций
    let operationCount = 0;
    let lastReset = Date.now();

    this.historyManager.on('operation-added', () => {
      operationCount++;
      
      // Сброс счетчика каждую минуту
      const now = Date.now();
      if (now - lastReset >= 60000) {
        this.metrics.operationsPerSecond = operationCount / 60;
        operationCount = 0;
        lastReset = now;
      }
    });
  }

  private updateMetrics(): void {
    const state = this.historyManager.getState();
    const operations = this.historyManager.getOperations();
    
    // Средний размер операции
    if (operations.length > 0) {
      const totalSize = operations.reduce((sum, op) => {
        return sum + JSON.stringify(op).length;
      }, 0);
      this.metrics.averageOperationSize = totalSize / operations.length;
    }

    // Использование памяти
    this.metrics.memoryUsage = state.totalSize;

    // Соотношение undo/redo
    if (state.stats.undoCount + state.stats.redoCount > 0) {
      this.metrics.undoRedoRatio = 
        state.stats.undoCount / (state.stats.undoCount + state.stats.redoCount);
    }
  }

  private logMetrics(): void {
    console.log('📊 История - метрики производительности:');
    console.log(`  Операций в секунду: ${this.metrics.operationsPerSecond.toFixed(2)}`);
    console.log(`  Средний размер операции: ${this.metrics.averageOperationSize.toFixed(0)} байт`);
    console.log(`  Использование памяти: ${(this.metrics.memoryUsage / 1024 / 1024).toFixed(2)} MB`);
    console.log(`  Соотношение Undo/Redo: ${(this.metrics.undoRedoRatio * 100).toFixed(1)}%`);
  }

  getMetrics() {
    return { ...this.metrics };
  }
}
```

## Экспорт и импорт истории

### Экспорт истории

```typescript
// Полный экспорт истории
const historyExport = historyManager.export();
console.log('Экспорт истории:', {
  version: historyExport.version,
  timestamp: new Date(historyExport.timestamp),
  operationsCount: historyExport.operations.length,
  snapshotsCount: historyExport.snapshots.length
});

// Сохранение в файл (Node.js)
import { writeFileSync } from 'fs';

const historyData = JSON.stringify(historyExport, null, 2);
writeFileSync('history-backup.json', historyData);
console.log('История сохранена в файл');

// Сохранение в localStorage (браузер)
localStorage.setItem('app-history', JSON.stringify(historyExport));
```

### Импорт истории

```typescript
// Загрузка из файла (Node.js)
import { readFileSync } from 'fs';

try {
  const historyData = readFileSync('history-backup.json', 'utf8');
  const importData = JSON.parse(historyData);
  
  historyManager.import(importData);
  console.log('История успешно импортирована');
} catch (error) {
  console.error('Ошибка импорта истории:', error);
}

// Загрузка из localStorage (браузер)
const savedHistory = localStorage.getItem('app-history');
if (savedHistory) {
  try {
    const importData = JSON.parse(savedHistory);
    historyManager.import(importData);
    console.log('История восстановлена из localStorage');
  } catch (error) {
    console.error('Ошибка восстановления истории:', error);
  }
}

// Слияние историй
class HistoryMerger {
  static merge(
    primary: HistoryExport, 
    secondary: HistoryExport
  ): HistoryExport {
    // Объединяем операции по времени
    const allOperations = [
      ...primary.operations,
      ...secondary.operations
    ].sort((a, b) => a.timestamp - b.timestamp);

    // Объединяем снимки
    const allSnapshots = [
      ...primary.snapshots,
      ...secondary.snapshots
    ].sort((a, b) => a.timestamp - b.timestamp);

    return {
      version: primary.version,
      timestamp: Date.now(),
      operations: allOperations,
      snapshots: allSnapshots,
      state: primary.state,
      config: primary.config
    };
  }
}
```

## Полный пример: Редактор документов

```typescript
class DocumentEditor {
  private document: CSElement;
  private historyManager: HistoryManagerImpl;
  private historyUI: HistoryUI;
  private monitor: HistoryMonitor;

  constructor() {
    this.initializeHistory();
    this.initializeDocument();
    this.setupAutoSave();
  }

  private initializeHistory(): void {
    // Конфигурация для редактора документов
    const config: HistoryConfig = {
      maxOperations: 1000,
      maxSize: 100 * 1024 * 1024, // 100MB
      snapshotInterval: 25,
      autoCleanup: true,
      compression: true,
      trackOperations: ['update', 'delete', 'create', 'format'],
      ignoreOperations: ['cursor', 'selection', 'scroll', 'focus']
    };

    this.historyManager = new HistoryManagerImpl(config);
    this.historyUI = new HistoryUI(this.historyManager);
    this.monitor = new HistoryMonitor(this.historyManager);

    // Обработка событий истории
    this.historyManager.on('operation-added', (data) => {
      this.onHistoryChanged('added', data.operation);
    });

    this.historyManager.on('undo-performed', (data) => {
      this.applyHistoryChange(data.operation.before);
      this.onHistoryChanged('undo', data.operation);
    });

    this.historyManager.on('redo-performed', (data) => {
      this.applyHistoryChange(data.operation.after);
      this.onHistoryChanged('redo', data.operation);
    });
  }

  private initializeDocument(): void {
    this.document = new CSElement('document');
    
    // Начальное содержимое
    this.document.setData('title', 'Untitled Document');
    this.document.setData('content', '');
    this.document.setData('metadata', {
      created: Date.now(),
      modified: Date.now(),
      version: '1.0.0'
    });
  }

  // Операции редактирования с отслеживанием истории
  async updateTitle(newTitle: string): Promise<void> {
    const oldTitle = this.document.getData('title');
    await this.document.setData('title', newTitle);
    
    this.historyManager.addOperation({
      type: 'update',
      description: `Changed title from "${oldTitle}" to "${newTitle}"`,
      before: { title: oldTitle },
      after: { title: newTitle },
      path: ['title'],
      canUndo: true,
      canRedo: true,
      metadata: {
        field: 'title',
        timestamp: Date.now()
      }
    });
  }

  async updateContent(newContent: string): Promise<void> {
    const oldContent = this.document.getData('content');
    await this.document.setData('content', newContent);
    
    // Создаем diff для больших изменений
    const diff = this.historyManager.createDiff(
      { content: oldContent },
      { content: newContent },
      ['content']
    );

    this.historyManager.addOperation({
      type: 'update',
      description: `Updated document content (${diff.length} changes)`,
      before: { content: oldContent },
      after: { content: newContent },
      path: ['content'],
      canUndo: true,
      canRedo: true,
      metadata: {
        field: 'content',
        diff: diff,
        timestamp: Date.now()
      }
    });
  }

  async formatText(range: { start: number; end: number }, format: any): Promise<void> {
    const content = this.document.getData('content');
    const formatting = this.document.getData('formatting') || {};
    
    const oldFormatting = { ...formatting };
    const newFormatting = {
      ...formatting,
      [range.start + '-' + range.end]: format
    };

    await this.document.setData('formatting', newFormatting);

    this.historyManager.addOperation({
      type: 'format',
      description: `Applied ${format.type} formatting to range ${range.start}-${range.end}`,
      before: { formatting: oldFormatting },
      after: { formatting: newFormatting },
      path: ['formatting'],
      canUndo: true,
      canRedo: true,
      metadata: {
        field: 'formatting',
        range: range,
        format: format,
        timestamp: Date.now()
      }
    });
  }

  // Применение изменений из истории
  private async applyHistoryChange(data: any): Promise<void> {
    if (data.title !== undefined) {
      await this.document.setData('title', data.title);
    }
    if (data.content !== undefined) {
      await this.document.setData('content', data.content);
    }
    if (data.formatting !== undefined) {
      await this.document.setData('formatting', data.formatting);
    }
    
    // Обновляем UI
    this.updateEditorUI();
  }

  private onHistoryChanged(type: string, operation: HistoryOperation): void {
    console.log(`История: ${type} - ${operation.description}`);
    
    // Обновляем метаданные документа
    const metadata = this.document.getData('metadata');
    this.document.setData('metadata', {
      ...metadata,
      modified: Date.now(),
      lastOperation: operation.description
    });
    
    // Показываем уведомление пользователю
    this.showNotification(`${type}: ${operation.description}`);
  }

  private setupAutoSave(): void {
    // Автосохранение каждые 30 секунд
    setInterval(async () => {
      const snapshot = this.historyManager.createSnapshot(
        this.document.export(),
        'Auto-save checkpoint'
      );
      
      // Сохраняем в localStorage
      const historyExport = this.historyManager.export();
      localStorage.setItem('document-history', JSON.stringify(historyExport));
      
      console.log('Автосохранение выполнено');
    }, 30000);
  }

  private updateEditorUI(): void {
    // Обновление UI редактора
    console.log('Обновление UI редактора');
  }

  private showNotification(message: string): void {
    // Показ уведомления пользователю
    console.log(`📝 ${message}`);
  }

  // Публичные методы для управления историей
  async undo(): Promise<void> {
    try {
      await this.historyManager.undo();
    } catch (error) {
      console.error('Ошибка отмены:', error);
    }
  }

  async redo(): Promise<void> {
    try {
      await this.historyManager.redo();
    } catch (error) {
      console.error('Ошибка повтора:', error);
    }
  }

  getHistoryState() {
    return this.historyManager.getState();
  }

  exportDocument() {
    return {
      document: this.document.export(),
      history: this.historyManager.export()
    };
  }

  importDocument(data: any) {
    // Импорт документа и истории
    this.document = CSElement.import(data.document);
    this.historyManager.import(data.history);
  }
}

// Использование
const editor = new DocumentEditor();

// Редактирование с отслеживанием истории
await editor.updateTitle('My Amazing Document');
await editor.updateContent('This is the content of my document...');
await editor.formatText({ start: 0, end: 10 }, { type: 'bold', value: true });

// Отмена последнего действия
await editor.undo();

// Повтор отмененного действия
await editor.redo();

// Проверка состояния истории
const state = editor.getHistoryState();
console.log(`Можно отменить: ${state.canUndo}, можно повторить: ${state.canRedo}`);
```

## 🔧 Лучшие практики

### 1. Оптимизация производительности
```typescript
// Используйте batch операции для групповых изменений
const batchOperations = [];
for (let i = 0; i < 100; i++) {
  batchOperations.push({
    type: 'update',
    elementId: `element-${i}`,
    before: oldValues[i],
    after: newValues[i]
  });
}

historyManager.addOperation({
  type: 'batch',
  description: 'Bulk update operation',
  before: batchOperations.map(op => op.before),
  after: batchOperations.map(op => op.after),
  canUndo: true,
  canRedo: true,
  metadata: { batchSize: batchOperations.length }
});
```

### 2. Управление памятью
```typescript
// Настройте ограничения для предотвращения утечек памяти
const memoryEfficientConfig: HistoryConfig = {
  maxOperations: 50,
  maxSize: 10 * 1024 * 1024, // 10MB
  autoCleanup: true,
  compression: true
};
```

### 3. Селективное отслеживание
```typescript
// Отслеживайте только важные операции
const selectiveConfig: HistoryConfig = {
  trackOperations: ['update', 'delete', 'create'],
  ignoreOperations: ['temp', 'ui-state', 'cursor-move']
};
```

History System в CSElement предоставляет полный контроль над историей изменений в ваших данных, обеспечивая надежную функциональность undo/redo и отслеживание всех операций! 📚✨ 