# Block-Based Conditional Execution Implementation

## Overview

This document explains the implementation of **block-based short-circuit execution** for IF-ELSEIF-ELSE conditionals in the Flutter milsat_modules package. This fixes the critical bug where all blocks in an IF-ELSEIF-ELSE chain could execute simultaneously instead of only the first matching block.

## The Problem

### Original Bug
The original `ConditionalModel.execute()` method evaluated the **entire** IF-ELSEIF-ELSE chain as a **single expression**:

```dart
// ❌ WRONG: Evaluates entire conditional as one expression
final result = evaluateCondition(bindings);
for (final effect in effects) {
  if (result == true) {
    effect.apply(); // ALL effects apply if result true
  }
}
```

This caused:
- **Race conditions**: Multiple blocks could fire simultaneously
- **SKIP vs SET conflicts**: One block SKIPs a field while another SETs it
- **Undefined behavior**: Last effect wins, but order is unpredictable

### Real-World Impact

Given this conditional:
```
IF RESIDENTIAL_STATUS EQUALS 'R'; SET CURRENT_USE = 'RES'
ELSEIF RESIDENTIAL_STATUS EQUALS 'C'; SET CURRENT_USE = 'COM'
ELSE; SET CURRENT_USE = 'UNK'
```

**Old Behavior** (BUG): If the expression evaluated to `true`, ALL effects could fire:
- `CURRENT_USE` gets SET to 'RES', then 'COM', then 'UNK'
- Final value is 'UNK' (last effect wins)
- Wrong answer!

**New Behavior** (CORRECT): Only first matching block fires:
- If `RESIDENTIAL_STATUS == 'R'`: SET to 'RES', STOP
- Else if `RESIDENTIAL_STATUS == 'C'`: SET to 'COM', STOP
- Else: SET to 'UNK', STOP

## The Solution

### Architecture

The solution involves three layers:

1. **Parsing Layer** (`ConditionalService`)
   - Parses DSL strings into structured blocks
   - Detects IF-ELSEIF-ELSE keywords using regex
   - Creates `ConditionalBlock` data structures

2. **Data Layer** (`ConditionalBlockModel`)
   - Represents individual IF/ELSEIF/ELSE blocks
   - Stores block type, conditions, effects, and code
   - Handles JSON serialization/deserialization

3. **Execution Layer** (`ConditionalModel`)
   - Implements block-by-block execution
   - Short-circuit evaluation: stops after first match
   - Maintains backward compatibility with simple IF conditionals

### Key Classes

#### ConditionalBlock (Service Layer)
```dart
class ConditionalBlock {
  final String blockType;           // "IF", "ELSEIF", "ELSE"
  final List<ConditionData> conditions;
  final String separatingOpcode;    // "AND" or "OR"
  final List<EffectData> effects;
  final String conditionalCode;     // Expression to evaluate
  
  bool get isElse => blockType.toUpperCase() == 'ELSE';
  List<String> get conditionColumns => ...
}
```

#### ConditionalBlockModel (Model Layer)
```dart
class ConditionalBlockModel {
  final String blockType;
  final List<String> conditionColumns;
  final List<EffectModel> effects;
  final String conditionalCode;
  
  bool get isElse => blockType.toUpperCase() == 'ELSE';
  
  factory ConditionalBlockModel.fromJson(Map<String, dynamic> json) { ... }
  factory ConditionalBlockModel.fromConditionalBlock(ConditionalBlock block) { ... }
}
```

#### ConditionalModel (Execution Layer)
```dart
class ConditionalModel extends ConditionalEntity {
  final List<ConditionalBlockModel>? blocks;
  
  bool get hasBlocks => blocks != null && blocks!.isNotEmpty;
  bool get isSimple => blocks == null || blocks!.isEmpty;
  
  @override
  bool execute(Map<String, BaseUIField> uiFieldMap) {
    if (hasBlocks) {
      return _executeBlocks(uiFieldMap);  // NEW: Block-based
    }
    return _executeSimple(uiFieldMap);    // OLD: Simple IF
  }
}
```

## Implementation Details

### 1. Block Parsing (ConditionalService)

The `parseDsl()` method now detects IF-ELSEIF-ELSE structures:

```dart
static ConditionalData parseDsl(String dsl) {
  // Check if this is a block-based conditional (IF-ELSEIF-ELSE)
  if (dsl.contains(RegExp(r'\bELSEIF\b|\bELSE\b', caseSensitive: false))) {
    return _parseDslBlocks(dsl);
  }
  
  // Parse as simple IF conditional (existing logic)
  return _parseDslSimple(dsl);
}
```

Block parsing uses regex patterns:
```dart
static ConditionalData _parseDslBlocks(String dsl) {
  final blocks = <ConditionalBlock>[];
  
  // Split on IF, ELSEIF, ELSE keywords
  final pattern = RegExp(
    r'(IF\s+.*?;[^;]*?(?=ELSEIF|ELSE|$))|'
    r'(ELSEIF\s+.*?;[^;]*?(?=ELSEIF|ELSE|$))|'
    r'(ELSE\s*;[^;]*)',
    caseSensitive: false,
    dotAll: true,
  );
  
  for (final match in pattern.allMatches(dsl)) {
    final blockStr = match.group(0)!.trim();
    final block = _parseBlock(blockStr);
    if (block != null) blocks.add(block);
  }
  
  return ConditionalData(blocks: blocks, ...);
}
```

### 2. Block-Based Execution

The critical fix is in `_executeBlocks()`:

```dart
bool _executeBlocks(Map<String, BaseUIField> uiFieldMap) {
  bool hasEndBeenTriggered = false;
  
  // SHORT-CIRCUIT: Loop through blocks, execute ONLY first matching one
  for (final block in blocks!) {
    final blockMatches = _evaluateBlock(block, uiFieldMap);
    
    if (blockMatches) {
      // This block matched - apply its effects and STOP
      hasEndBeenTriggered = _applyBlockEffects(block, uiFieldMap);
      break; // ✅ CRITICAL: Stop after first match
    }
  }
  
  return hasEndBeenTriggered;
}
```

### 3. Block Evaluation

Each block is evaluated independently:

```dart
bool _evaluateBlock(ConditionalBlockModel block, Map<String, BaseUIField> uiFieldMap) {
  // ELSE blocks always match
  if (block.isElse) {
    return true;
  }
  
  // Collect field values for this block's conditions
  final bindings = <String, dynamic>{};
  for (final columnName in block.conditionColumns) {
    final uiField = uiFieldMap[columnName];
    if (uiField == null) continue;
    
    final state = (uiField.key as GlobalKey).currentState as BaseUIFieldState?;
    var fieldValue = state?.getContent().$2;
    
    // Handle MultiChoice sorting
    if (uiField is MilsatMultichoiceWidget && fieldValue != null) {
      final valueCharArray = fieldValue.split('');
      valueCharArray.sort();
      fieldValue = valueCharArray.join('');
    }
    
    bindings[columnName] = fieldValue;
  }
  
  // Evaluate THIS block's condition (not the entire conditional!)
  return _evaluateCondition(block.conditionalCode, bindings);
}
```

### 4. Effect Application

Only the matching block's effects are applied:

```dart
bool _applyBlockEffects(ConditionalBlockModel block, Map<String, BaseUIField> uiFieldMap) {
  bool hasEndBeenTriggered = false;
  
  for (final effect in block.effects) {
    final effectUIField = uiFieldMap[effect.columnName];
    final effectState = effectUIField?.key != null
        ? ((effectUIField!.key as GlobalKey).currentState as BaseUIFieldState?)
        : null;
    
    switch (effect.opCode.toUpperCase()) {
      case "SKIP":
        effectState?.skip();
        break;
        
      case "SET":
        effectState?.setContent(effect.value);
        break;
        
      case "END":
        hasEndBeenTriggered = true;
        break;
        
      case "LOOPNEXT":
        break;
    }
  }
  
  return hasEndBeenTriggered;
}
```

## Backward Compatibility

The implementation maintains **full backward compatibility** with simple IF conditionals:

```dart
@override
bool execute(Map<String, BaseUIField> uiFieldMap) {
  // NEW: Block-based execution (IF-ELSEIF-ELSE)
  if (hasBlocks) {
    return _executeBlocks(uiFieldMap);
  }
  
  // OLD: Simple IF execution (backward compatible)
  return _executeSimple(uiFieldMap);
}
```

Simple IF conditionals (without ELSEIF/ELSE) continue to work exactly as before through `_executeSimple()`.

## Testing Considerations

### Unit Tests Needed

1. **Block Parsing Tests**
   - Parse IF-ELSEIF-ELSE with multiple blocks
   - Parse ELSE blocks (no condition)
   - Parse nested AND/OR conditions
   - Handle malformed DSL strings

2. **Execution Tests**
   - Verify only first matching block executes
   - Verify ELSE block executes when no IF/ELSEIF matches
   - Verify short-circuit behavior (subsequent blocks ignored)
   - Test with hierarchical dependencies (user's 112403_2, 112403_3)

3. **Effect Application Tests**
   - SKIP effects only applied from matching block
   - SET effects only applied from matching block
   - Multiple effects in one block all apply
   - Effects from non-matching blocks never apply

4. **Backward Compatibility Tests**
   - Simple IF conditionals still work
   - Legacy JSON configs load correctly
   - No breaking changes to existing forms

### Integration Tests Needed

1. Load user's real conditionals (112403_2, 112403_3)
2. Test hierarchical dependencies:
   - RESIDENTIAL_STATUS → CURRENT_USE
   - CURRENT_USE → HH_STATUS
3. Verify no SKIP vs SET conflicts
4. Verify proper field skipping/setting based on values

## Related Documentation

- **CONDITIONAL_SYSTEM_FIXES.md**: Kotlin validator enhancements for detecting SKIP vs SET conflicts
- **CONDITIONAL_BEST_PRACTICES.md**: Guidelines for creating hierarchical and dependent conditionals
- **Conditional Service**: `/lib/src/features/common/domain/services/conditional_service.dart`
- **Conditional Model**: `/lib/src/features/common/data/models/form/conditional_model.dart`

## Future Enhancements

1. **Performance Optimization**
   - Cache parsed blocks to avoid re-parsing
   - Pre-compile expressions for faster evaluation
   - Parallelize independent condition evaluations

2. **Enhanced Debugging**
   - Log which block matched and executed
   - Visualize conditional execution flow
   - Show block evaluation results in dev tools

3. **Validator Integration**
   - Implement Kotlin validator SKIP vs SET detection
   - Add Flutter-side validation warnings
   - Detect redundant ELSEIF conditions that can never match

4. **Expression Enhancement**
   - Support more complex expressions (CONTAINS, REGEX, etc.)
   - Add functions (LENGTH, UPPERCASE, etc.)
   - Better error messages for invalid expressions

## Migration Guide

Existing configs require **no changes**. The system automatically detects:

- **Simple IF**: Uses `_executeSimple()` (old behavior)
- **IF-ELSEIF-ELSE**: Uses `_executeBlocks()` (new behavior)

To create new IF-ELSEIF-ELSE conditionals, use this format:

```json
{
  "conditionalDsl": "IF FIELD_A EQUALS 'X'; SET FIELD_B = 'Y' ELSEIF FIELD_A EQUALS 'Z'; SET FIELD_B = 'W' ELSE; SKIP FIELD_B"
}
```

The parser will automatically detect the ELSEIF/ELSE keywords and create blocks.

## Summary

This implementation fixes the critical race condition bug by:

1. ✅ Parsing IF-ELSEIF-ELSE into separate blocks
2. ✅ Evaluating blocks sequentially (not as one expression)
3. ✅ Applying only first matching block's effects
4. ✅ Short-circuiting: stopping after first match
5. ✅ Maintaining backward compatibility with simple IF conditionals

The result is **predictable, deterministic conditional execution** that matches developer expectations and eliminates SKIP vs SET conflicts.
