import 'package:dart_framework/utils/system_util.dart';
import 'package:oto/oto/data/pipeline_data.dart';
import 'package:oto/oto/pipeline/pipeline.dart';
import 'package:oto/oto/pipeline/pipeline_condition.dart';
import 'package:oto/oto/pipeline/pipeline_exe.dart';

/// Polls every 200ms and blocks until the condition becomes false.
/// Use wait-until-int / wait-until-float / wait-until-double /
/// wait-until-string depending on type.
///
/// Example YAML:
/// ```yaml
/// - wait-until-string: "<!state.upload> != complete" # waits while condition holds
/// ```
class PipelineWaitUntil extends PipelineCondition {
  PipelineWaitUntil(String type) {
    data = DataCondition();
    data.type = type;
  }

  @override
  PipelineValidateResult initialize(Map<String, dynamic> set) {
    final value = set.values.first;
    if (value is! String) {
      final result = PipelineValidateResult();
      result.enable = false;
      result.message =
          'wait-until requires a string condition expression, got: ${value.runtimeType}.';
      return result;
    }
    data.condition = value;
    var result = super.initialize(set);
    if (!result.enable) {
      return result;
    }
    return validateConditionExpression('wait-until');
  }

  @override
  Future execute() async {
    var condition = data.condition!;
    var result = checkCondition(condition);
    await PipelineExecutor.printCondition(
        'Wait-Until', condition, 'Start wait.', result);
    while (result.result) {
      await Future.delayed(const Duration(milliseconds: 200));
      result = checkCondition(data.condition!);
    }
    await PipelineExecutor.printCondition(
        'Wait-Until', condition, 'Loop escape.', result);
    return simpleFuture;
  }
}

/// Waits for a fixed number of seconds (supports fractional values).
///
/// Example YAML:
/// ```yaml
/// - wait-until-seconds: 2.5
/// ```
class PipelineWaitUntilSeconds extends PipelineExecutor {
  PipelineWaitUntilSeconds();

  late int milliseconds;

  @override
  PipelineValidateResult initialize(Map<String, dynamic> set) {
    var value = set.values.first.toString();
    var seconds = double.tryParse(value);
    if (seconds == null) {
      var result = PipelineValidateResult();
      result.message = 'Failed to cast as double value: "$value"';
      result.enable = false;
      return result;
    }
    milliseconds = (seconds * 1000).round();
    return PipelineValidateResult();
  }

  @override
  Future execute() async {
    await Future.delayed(Duration(milliseconds: milliseconds));
    return simpleFuture;
  }
}
