// ignore_for_file: unnecessary_cast, unnecessary_null_comparison

import 'package:dart_framework/core/app_data_manager.dart';
import 'package:oto/oto/core/tag_system.dart';
import 'package:oto/oto/data/pipeline_data.dart';
import 'package:oto/oto/pipeline/pipeline.dart';
import 'package:oto/oto/pipeline/pipeline_exe.dart';

abstract class PipelineCondition extends PipelineExecutor {
  late DataCondition data;
  Map<String, bool Function(dynamic, dynamic, dynamic Function(dynamic))>
      conditions = {};
  Map<String, dynamic Function(dynamic value)> typeCaster = {};

  @override
  PipelineValidateResult initialize(Map<String, dynamic> set) {
    conditions = {
      ' < ': (v0, v1, caster) => caster(v0) < caster(v1),
      ' <= ': (v0, v1, caster) => caster(v0) <= caster(v1),
      ' > ': (v0, v1, caster) => caster(v0) > caster(v1),
      ' >= ': (v0, v1, caster) => caster(v0) >= caster(v1),
      ' == ': (v0, v1, caster) => caster(v0) == caster(v1),
      ' != ': (v0, v1, caster) => caster(v0) != caster(v1),
    };
    typeCaster = {
      'int': (value) => (value == 'null' || value == null)
          ? null
          : (value is String)
              ? int.parse(value) as int
              : value,
      'float': (value) => (value == 'null' || value == null)
          ? null
          : (value is String)
              ? double.parse(value) as double?
              : value,
      'double': (value) => (value == 'null' || value == null)
          ? null
          : (value is String)
              ? double.parse(value) as double?
              : value,
      'string': (value) =>
          (value == 'null' || value == null) ? null : value.toString(),
      'bool': (value) => (value == 'null' || value == null)
          ? null
          : (value is String)
              ? (value.toLowerCase() == 'true') as bool?
              : value,
      'date': (value) => (value == 'null' || value == null)
          ? null
          : DateTime.parse(value).microsecondsSinceEpoch,
      'version': (value) => (value == 'null' || value == null)
          ? null
          : AppDataManager.to.versionToNumber(value) as double?,
      'object': (value) => (value == 'null' || value == null) ? null : value,
    };
    return PipelineValidateResult();
  }

  PipelineValidateResult validateConditionExpression(String syntax) {
    var result = PipelineValidateResult();
    final expression = data.condition;
    final hasOperator = expression != null &&
        conditions.keys.any((operator) => expression.contains(operator));
    if (!hasOperator) {
      result.enable = false;
      result.message =
          '$syntax condition must contain one supported operator: ${conditions.keys.map((e) => e.trim()).join(', ')}.';
    }
    return result;
  }

  ConditionCheckResult checkCondition(String condition) {
    var result = ConditionCheckResult();
    for (var item in conditions.entries) {
      if (condition.contains(item.key)) {
        var list = condition.split(item.key);
        dynamic value0;
        dynamic value1;
        if (data.type == 'object') {
          value0 =
              TagSystem.replaceSingleTagValue(list[0], context: runtimeContext);
          value1 =
              TagSystem.replaceSingleTagValue(list[1], context: runtimeContext);
          result.conditionValue =
              '${value0.toString()}${item.key}${value1.toString()}';
        } else {
          value0 = TagSystem.replaceTagValue(list[0], context: runtimeContext);
          value1 = TagSystem.replaceTagValue(list[1], context: runtimeContext);
          result.conditionValue = '$value0${item.key}$value1';
        }
        result.result = item.value(value0, value1, typeCaster[data.type]!);

        return result;
      }
    }
    return result;
  }

  @override
  Future execute();
}

class ConditionCheckResult {
  late String conditionValue;
  late bool result;
}
