import 'package:guru_utils/id/identifiable.dart';
import 'package:rxdart/rxdart.dart';

import 'batch_data.dart';

/// Created by @Haoyi on 2021/7/25
///

mixin BatchAware<T extends Identifiable> {
  final BehaviorSubject<Map<String, T>> _subject = BehaviorSubject.seeded(<String, T>{});

  Stream<Map<String, T>> get observableData => _subject.stream;

  T? getData(String key) => _subject.value[key];

  bool exists(String key) => _subject.value.containsKey(key);

  List<T> get dataList => _subject.value.values.toList() ?? [];

  bool get isDataEmpty => _subject.value.isEmpty;

  Map<String, T> get allData => _subject.value;

  void touch() {
    _subject.add(_subject.value);
  }

  void processBatchData(BatchData<T> batchData) async {
    for (var action in batchData.getActions()) {
      switch (action.method) {
        case BatchMethod.insert:
        case BatchMethod.update:
        case BatchMethod.select:
          final changedData = Map<String, T>.from(_subject.value);
          for (var entity in action.collection) {
            changedData[entity.id] = entity;
          }
          _subject.add(changedData);
          break;
        case BatchMethod.delete:
          final changedData = Map<String, T>.from(_subject.value);
          bool changed = false;
          for (var entity in action.collection) {
            if (changedData.containsKey(entity.id)) {
              changedData.remove(entity.id);
              changed = true;
            }
          }
          if (changed) {
            _subject.add(changedData);
          }
          break;
        case BatchMethod.clear:
          _subject.add(<String, T>{});
          break;
        case BatchMethod.remove:
          final copiedData = Map<String, T>.from(_subject.value);
          final reservedData = <String, T>{};
          for (var entity in copiedData.values) {
            if (action.where?.call(entity) == false) {
              reservedData[entity.id] = entity;
            }
          }
          _subject.add(reservedData);
          break;
        default:
          break;
      }
    }
  }

  void disposeBatch() {
    _subject.close();
  }
}
