# 🔄 State Machine и управление состояниями

CSElement включает мощную систему State Machine для управления состояниями элементов с поддержкой переходов, условий и действий.

## 📋 Содержание

- [Основы State Machine](#основы-state-machine)
- [Создание состояний и переходов](#создание-состояний-и-переходов)
- [События и действия](#события-и-действия)
- [Условия и охранники](#условия-и-охранники)
- [Полный пример](#полный-пример-система-заказов)

## Основы State Machine

### Создание State Machine

```typescript
import { 
  StateMachine, 
  StateMachineConfig,
  StateMachineEventType,
  State,
  Transition 
} from 'cs-element';

// Конфигурация State Machine
const config: StateMachineConfig = {
  name: 'OrderStateMachine',
  initialState: 'draft',
  enableLogging: true,
  enableProfiling: true,
  states: [
    {
      name: 'draft',
      description: 'Черновик заказа',
      type: 'normal',
      onEnter: async (context) => {
        console.log('📝 Заказ переведен в черновик');
        await context.element.setData('status', 'draft');
      },
      onExit: async (context) => {
        console.log('📝 Выход из состояния черновика');
      }
    },
    {
      name: 'pending',
      description: 'Ожидает подтверждения',
      type: 'normal',
      onEnter: async (context) => {
        console.log('⏳ Заказ ожидает подтверждения');
        await context.element.setData('status', 'pending');
        await context.element.setData('submittedAt', new Date().toISOString());
      }
    },
    {
      name: 'confirmed',
      description: 'Подтвержден',
      type: 'normal',
      onEnter: async (context) => {
        console.log('✅ Заказ подтвержден');
        await context.element.setData('status', 'confirmed');
        await context.element.setData('confirmedAt', new Date().toISOString());
      }
    },
    {
      name: 'cancelled',
      description: 'Отменен',
      type: 'final',
      onEnter: async (context) => {
        console.log('❌ Заказ отменен');
        await context.element.setData('status', 'cancelled');
        await context.element.setData('cancelledAt', new Date().toISOString());
      }
    }
  ],
  transitions: [
    {
      id: 'submit',
      from: 'draft',
      to: 'pending',
      event: 'SUBMIT',
      description: 'Отправка заказа на подтверждение',
      priority: 1
    },
    {
      id: 'confirm',
      from: 'pending',
      to: 'confirmed',
      event: 'CONFIRM',
      description: 'Подтверждение заказа',
      priority: 1
    },
    {
      id: 'cancel-draft',
      from: 'draft',
      to: 'cancelled',
      event: 'CANCEL',
      description: 'Отмена черновика',
      priority: 2
    },
    {
      id: 'cancel-pending',
      from: 'pending',
      to: 'cancelled',
      event: 'CANCEL',
      description: 'Отмена ожидающего заказа',
      priority: 2
    }
  ]
};

// Создание элемента и State Machine
const orderElement = new CSElement('order-123');
const stateMachine = new StateMachine(orderElement, config);
```

### Отправка событий

```typescript
// Отправка заказа
const submitResult = await stateMachine.send('SUBMIT');
console.log('Результат отправки:', submitResult);

// Подтверждение заказа
const confirmResult = await stateMachine.send('CONFIRM');
console.log('Результат подтверждения:', confirmResult);

// Отмена заказа
const cancelResult = await stateMachine.send('CANCEL');
console.log('Результат отмены:', cancelResult);

// Получение текущего состояния
console.log('Текущее состояние:', stateMachine.currentState);

// Получение возможных переходов
const possibleTransitions = stateMachine.getPossibleTransitions();
console.log('Возможные переходы:', possibleTransitions.map(t => t.event));
```

## Создание состояний и переходов

### Динамическое создание состояний

```typescript
class OrderStateMachineBuilder {
  private states: State[] = [];
  private transitions: Transition[] = [];

  addState(name: string, config: {
    description?: string;
    type?: 'normal' | 'initial' | 'final';
    onEnter?: (context: any) => Promise<void>;
    onExit?: (context: any) => Promise<void>;
  }): this {
    this.states.push({
      name,
      description: config.description || name,
      type: config.type || 'normal',
      onEnter: config.onEnter,
      onExit: config.onExit
    });
    return this;
  }

  addTransition(from: string, to: string, event: string, config: {
    description?: string;
    priority?: number;
    condition?: (context: any, event: any) => Promise<boolean>;
    action?: (context: any, event: any) => Promise<void>;
  } = {}): this {
    this.transitions.push({
      id: `${from}-${to}-${event}`,
      from,
      to,
      event,
      description: config.description || `${from} → ${to}`,
      priority: config.priority || 1,
      condition: config.condition,
      action: config.action
    });
    return this;
  }

  build(element: CSElement, name: string, initialState: string): StateMachine {
    const config: StateMachineConfig = {
      name,
      initialState,
      enableLogging: true,
      states: this.states,
      transitions: this.transitions
    };

    return new StateMachine(element, config);
  }
}

// Использование builder'а
const orderSM = new OrderStateMachineBuilder()
  .addState('draft', {
    description: 'Черновик заказа',
    onEnter: async (context) => {
      await context.element.setData('status', 'draft');
    }
  })
  .addState('processing', {
    description: 'Обработка заказа',
    onEnter: async (context) => {
      await context.element.setData('status', 'processing');
      await context.element.setData('processingStarted', new Date().toISOString());
    }
  })
  .addState('completed', {
    description: 'Заказ выполнен',
    type: 'final',
    onEnter: async (context) => {
      await context.element.setData('status', 'completed');
      await context.element.setData('completedAt', new Date().toISOString());
    }
  })
  .addTransition('draft', 'processing', 'START_PROCESSING', {
    description: 'Начать обработку заказа',
    condition: async (context) => {
      const items = await context.element.getData('items') as any[];
      return items && items.length > 0;
    },
    action: async (context) => {
      console.log('🚀 Начинаем обработку заказа');
      await context.element.setData('processingBy', 'system');
    }
  })
  .addTransition('processing', 'completed', 'COMPLETE', {
    description: 'Завершить заказ',
    action: async (context) => {
      console.log('✅ Заказ завершен');
      await context.element.setData('completedBy', 'system');
    }
  })
  .build(orderElement, 'OrderProcessor', 'draft');
```

## События и действия

### Обработка событий

```typescript
// Подписка на события State Machine
stateMachine.subscribe(StateMachineEventType.TRANSITION_STARTED, (data) => {
  console.log(`🔄 Начался переход: ${data.fromState} → ${data.toState}`);
});

stateMachine.subscribe(StateMachineEventType.TRANSITION_COMPLETED, (data) => {
  console.log(`✅ Переход завершен: ${data.fromState} → ${data.toState} (${data.duration}ms)`);
});

stateMachine.subscribe(StateMachineEventType.TRANSITION_FAILED, (data) => {
  console.error(`❌ Переход неудачен: ${data.fromState} → ${data.toState}`, data.error);
});

stateMachine.subscribe(StateMachineEventType.STATE_ENTERED, (data) => {
  console.log(`📍 Вошли в состояние: ${data.state}`);
});

stateMachine.subscribe(StateMachineEventType.STATE_EXITED, (data) => {
  console.log(`📤 Покинули состояние: ${data.state}`);
});
```

### Сложные действия

```typescript
// Переход с асинхронными действиями
const complexTransition: Transition = {
  id: 'process-payment',
  from: 'pending',
  to: 'paid',
  event: 'PROCESS_PAYMENT',
  description: 'Обработка платежа',
  condition: async (context, event) => {
    const amount = await context.element.getData('amount') as number;
    const paymentMethod = event.payload?.paymentMethod;
    
    return amount > 0 && paymentMethod;
  },
  action: async (context, event) => {
    const amount = await context.element.getData('amount') as number;
    const paymentMethod = event.payload.paymentMethod;
    
    // Имитация обработки платежа
    console.log(`💳 Обработка платежа ${amount} через ${paymentMethod}`);
    
    // Сохраняем детали платежа
    await context.element.setData('paymentMethod', paymentMethod);
    await context.element.setData('paymentProcessedAt', new Date().toISOString());
    
    // Имитация внешнего API
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    if (Math.random() > 0.1) { // 90% успеха
      await context.element.setData('paymentStatus', 'success');
      console.log('✅ Платеж успешно обработан');
    } else {
      await context.element.setData('paymentStatus', 'failed');
      throw new Error('Ошибка обработки платежа');
    }
  }
};

// Отправка события с данными
const paymentResult = await stateMachine.send('PROCESS_PAYMENT', {
  paymentMethod: 'credit_card',
  cardNumber: '**** **** **** 1234'
});
```

## Условия и охранники

### Условные переходы

```typescript
// Переход с проверкой условий
const conditionalTransition: Transition = {
  id: 'approve-order',
  from: 'pending',
  to: 'approved',
  event: 'APPROVE',
  description: 'Одобрение заказа',
  condition: async (context, event) => {
    const amount = await context.element.getData('amount') as number;
    const userRole = event.payload?.userRole;
    
    // Менеджеры могут одобрять заказы до 10000
    if (userRole === 'manager' && amount <= 10000) {
      return true;
    }
    
    // Директора могут одобрять любые заказы
    if (userRole === 'director') {
      return true;
    }
    
    return false;
  },
  action: async (context, event) => {
    const approver = event.payload.userRole;
    await context.element.setData('approvedBy', approver);
    await context.element.setData('approvedAt', new Date().toISOString());
    
    console.log(`✅ Заказ одобрен пользователем с ролью: ${approver}`);
  }
};

// Использование условного перехода
const approvalResult = await stateMachine.send('APPROVE', {
  userRole: 'manager',
  userId: 'user-123'
});
```

### Сложные условия

```typescript
class OrderValidator {
  static async validateOrderItems(context: any): Promise<boolean> {
    const items = await context.element.getData('items') as any[];
    
    if (!items || items.length === 0) {
      return false;
    }
    
    // Проверяем каждый товар
    for (const item of items) {
      if (!item.id || !item.quantity || item.quantity <= 0) {
        return false;
      }
      
      // Проверяем наличие на складе
      const inStock = await this.checkInventory(item.id, item.quantity);
      if (!inStock) {
        return false;
      }
    }
    
    return true;
  }
  
  static async validateCustomer(context: any): Promise<boolean> {
    const customerId = await context.element.getData('customerId') as string;
    
    if (!customerId) {
      return false;
    }
    
    // Проверяем статус клиента
    const customerStatus = await this.getCustomerStatus(customerId);
    return customerStatus === 'active';
  }
  
  static async validatePayment(context: any): Promise<boolean> {
    const amount = await context.element.getData('amount') as number;
    const paymentMethod = await context.element.getData('paymentMethod') as string;
    
    if (!amount || amount <= 0) {
      return false;
    }
    
    if (!paymentMethod) {
      return false;
    }
    
    // Проверяем лимиты платежей
    return amount <= 100000; // максимум 100,000
  }
  
  private static async checkInventory(itemId: string, quantity: number): Promise<boolean> {
    // Имитация проверки склада
    return Math.random() > 0.1; // 90% товаров в наличии
  }
  
  private static async getCustomerStatus(customerId: string): Promise<string> {
    // Имитация проверки статуса клиента
    return Math.random() > 0.05 ? 'active' : 'blocked';
  }
}

// Использование валидатора в переходах
const validateTransition: Transition = {
  id: 'validate-order',
  from: 'draft',
  to: 'validated',
  event: 'VALIDATE',
  description: 'Валидация заказа',
  condition: async (context, event) => {
    const itemsValid = await OrderValidator.validateOrderItems(context);
    const customerValid = await OrderValidator.validateCustomer(context);
    const paymentValid = await OrderValidator.validatePayment(context);
    
    return itemsValid && customerValid && paymentValid;
  },
  action: async (context, event) => {
    await context.element.setData('validatedAt', new Date().toISOString());
    await context.element.setData('validatedBy', 'system');
    
    console.log('✅ Заказ успешно валидирован');
  }
};
```

## Полный пример: Система заказов

```typescript
class OrderManagementSystem {
  private orders: Map<string, { element: CSElement; stateMachine: StateMachine }> = new Map();
  
  async createOrder(orderData: {
    customerId: string;
    items: Array<{ id: string; quantity: number; price: number }>;
    paymentMethod: string;
  }): Promise<string> {
    const orderId = `order-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    
    // Создаем элемент заказа
    const orderElement = new CSElement(orderId);
    await orderElement.setData('customerId', orderData.customerId);
    await orderElement.setData('items', orderData.items);
    await orderElement.setData('paymentMethod', orderData.paymentMethod);
    
    const totalAmount = orderData.items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
    await orderElement.setData('amount', totalAmount);
    await orderElement.setData('createdAt', new Date().toISOString());
    
    // Создаем State Machine
    const stateMachine = this.createOrderStateMachine(orderElement);
    
    // Сохраняем заказ
    this.orders.set(orderId, { element: orderElement, stateMachine });
    
    console.log(`🛒 Заказ ${orderId} создан на сумму ${totalAmount}`);
    return orderId;
  }
  
  private createOrderStateMachine(element: CSElement): StateMachine {
    const config: StateMachineConfig = {
      name: 'OrderLifecycle',
      initialState: 'draft',
      enableLogging: true,
      states: [
        {
          name: 'draft',
          description: 'Черновик заказа',
          type: 'initial',
          onEnter: async (context) => {
            await context.element.setData('status', 'draft');
          }
        },
        {
          name: 'validated',
          description: 'Валидирован',
          type: 'normal',
          onEnter: async (context) => {
            await context.element.setData('status', 'validated');
          }
        },
        {
          name: 'payment_processing',
          description: 'Обработка платежа',
          type: 'normal',
          onEnter: async (context) => {
            await context.element.setData('status', 'payment_processing');
          }
        },
        {
          name: 'paid',
          description: 'Оплачен',
          type: 'normal',
          onEnter: async (context) => {
            await context.element.setData('status', 'paid');
          }
        },
        {
          name: 'processing',
          description: 'В обработке',
          type: 'normal',
          onEnter: async (context) => {
            await context.element.setData('status', 'processing');
          }
        },
        {
          name: 'shipped',
          description: 'Отправлен',
          type: 'normal',
          onEnter: async (context) => {
            await context.element.setData('status', 'shipped');
          }
        },
        {
          name: 'delivered',
          description: 'Доставлен',
          type: 'final',
          onEnter: async (context) => {
            await context.element.setData('status', 'delivered');
            await context.element.setData('deliveredAt', new Date().toISOString());
          }
        },
        {
          name: 'cancelled',
          description: 'Отменен',
          type: 'final',
          onEnter: async (context) => {
            await context.element.setData('status', 'cancelled');
            await context.element.setData('cancelledAt', new Date().toISOString());
          }
        }
      ],
      transitions: [
        {
          id: 'validate',
          from: 'draft',
          to: 'validated',
          event: 'VALIDATE',
          condition: async (context) => {
            return await OrderValidator.validateOrderItems(context) &&
                   await OrderValidator.validateCustomer(context) &&
                   await OrderValidator.validatePayment(context);
          }
        },
        {
          id: 'process-payment',
          from: 'validated',
          to: 'payment_processing',
          event: 'PROCESS_PAYMENT'
        },
        {
          id: 'payment-success',
          from: 'payment_processing',
          to: 'paid',
          event: 'PAYMENT_SUCCESS',
          action: async (context) => {
            await context.element.setData('paidAt', new Date().toISOString());
          }
        },
        {
          id: 'start-processing',
          from: 'paid',
          to: 'processing',
          event: 'START_PROCESSING'
        },
        {
          id: 'ship',
          from: 'processing',
          to: 'shipped',
          event: 'SHIP',
          action: async (context, event) => {
            await context.element.setData('trackingNumber', event.payload?.trackingNumber);
            await context.element.setData('shippedAt', new Date().toISOString());
          }
        },
        {
          id: 'deliver',
          from: 'shipped',
          to: 'delivered',
          event: 'DELIVER'
        },
        {
          id: 'cancel',
          from: 'draft',
          to: 'cancelled',
          event: 'CANCEL'
        },
        {
          id: 'cancel-validated',
          from: 'validated',
          to: 'cancelled',
          event: 'CANCEL'
        }
      ]
    };
    
    return new StateMachine(element, config);
  }
  
  async processOrder(orderId: string, event: string, payload?: any): Promise<any> {
    const order = this.orders.get(orderId);
    if (!order) {
      throw new Error(`Заказ ${orderId} не найден`);
    }
    
    const result = await order.stateMachine.send(event, payload);
    
    if (result.success) {
      console.log(`✅ Событие ${event} для заказа ${orderId} обработано успешно`);
    } else {
      console.error(`❌ Ошибка обработки события ${event} для заказа ${orderId}:`, result.error);
    }
    
    return result;
  }
  
  async getOrderStatus(orderId: string): Promise<{
    id: string;
    status: string;
    currentState: string;
    possibleActions: string[];
    history: any[];
  }> {
    const order = this.orders.get(orderId);
    if (!order) {
      throw new Error(`Заказ ${orderId} не найден`);
    }
    
    const status = await order.element.getData('status') as string;
    const possibleTransitions = order.stateMachine.getPossibleTransitions();
    const history = order.stateMachine.getHistory();
    
    return {
      id: orderId,
      status,
      currentState: order.stateMachine.currentState,
      possibleActions: possibleTransitions.map(t => t.event),
      history
    };
  }
  
  async getAllOrders(): Promise<Array<{
    id: string;
    status: string;
    amount: number;
    createdAt: string;
  }>> {
    const orders = [];
    
    for (const [orderId, order] of this.orders) {
      const status = await order.element.getData('status') as string;
      const amount = await order.element.getData('amount') as number;
      const createdAt = await order.element.getData('createdAt') as string;
      
      orders.push({
        id: orderId,
        status,
        amount,
        createdAt
      });
    }
    
    return orders;
  }
}

// Демонстрация использования
async function demonstrateOrderSystem() {
  const orderSystem = new OrderManagementSystem();
  
  try {
    // Создаем заказ
    const orderId = await orderSystem.createOrder({
      customerId: 'customer-123',
      items: [
        { id: 'item-1', quantity: 2, price: 100 },
        { id: 'item-2', quantity: 1, price: 200 }
      ],
      paymentMethod: 'credit_card'
    });
    
    // Валидируем заказ
    await orderSystem.processOrder(orderId, 'VALIDATE');
    
    // Обрабатываем платеж
    await orderSystem.processOrder(orderId, 'PROCESS_PAYMENT');
    await orderSystem.processOrder(orderId, 'PAYMENT_SUCCESS');
    
    // Начинаем обработку
    await orderSystem.processOrder(orderId, 'START_PROCESSING');
    
    // Отправляем заказ
    await orderSystem.processOrder(orderId, 'SHIP', {
      trackingNumber: 'TRACK123456'
    });
    
    // Доставляем заказ
    await orderSystem.processOrder(orderId, 'DELIVER');
    
    // Проверяем финальный статус
    const finalStatus = await orderSystem.getOrderStatus(orderId);
    console.log('📊 Финальный статус заказа:', finalStatus);
    
    // Получаем все заказы
    const allOrders = await orderSystem.getAllOrders();
    console.log('📋 Все заказы:', allOrders);
    
  } catch (error) {
    console.error('❌ Ошибка в системе заказов:', error);
  }
}

demonstrateOrderSystem().catch(console.error);
```

## 🎯 Заключение

State Machine в CSElement обеспечивает:

- **🔄 Четкое управление состояниями** - структурированные переходы между состояниями
- **🛡️ Валидация переходов** - условия и охранники для безопасных переходов
- **⚡ Асинхронные действия** - поддержка сложных операций при переходах
- **📊 Мониторинг** - отслеживание истории переходов и событий
- **🔧 Гибкость** - динамическое создание состояний и переходов

---

**Следующий раздел:** [Diff Engine и слияние данных →](12-diff-engine.md) 