import 'package:dart_framework/utils/system_util.dart';
import 'package:oto/cli/cli.dart';
import 'package:oto/oto/application.dart';
import 'package:oto/oto/commands/command.dart';
import 'package:oto/oto/data/command_data.dart';
import 'package:oto/oto/data/pipeline_data.dart';
import 'package:oto/oto/pipeline/pipeline.dart';
import 'package:oto/oto/pipeline/pipeline_exe.dart';

class ExeHandleData {
  late Pipeline? pipelineSuccess;
  late Pipeline? pipelineFail;
}

/// Executes a command and branches to on-success or on-fail pipeline based on result.
///
/// Example YAML:
/// ```yaml
/// - exe-handle:
///     id: buildIos
///     on-success:
///       - exe: notifySuccess
///     on-fail:
///       - exe: notifyFail
/// ```
class PipelineExeHandle extends PipelineExecutor {
  late DataExeHandle data;
  late String commandID;
  late ExeHandleData handleData;

  @override
  String? get stepCommandId => commandID;

  @override
  String? get stepCommandType =>
      runtimeContext.dataCommandMap[commandID]?.command.name;

  @override
  PipelineValidateResult initialize(Map<String, dynamic> set) {
    var result = PipelineValidateResult();
    final raw = set.values.first;
    if (raw is! Map) {
      result.enable = false;
      result.message = 'exe-handle: value must be a map.';
      return result;
    }
    final rawMap = Map<String, dynamic>.from(raw);
    final idVal = rawMap['id'];
    if (idVal != null && idVal is! String) {
      result.enable = false;
      result.message =
          'exe-handle: id must be a string, got: ${idVal.runtimeType}.';
      return result;
    }
    final onSuccess = rawMap['on-success'];
    if (onSuccess != null && onSuccess is! List) {
      result.enable = false;
      result.message =
          'exe-handle: "on-success" must be a list, got: ${onSuccess.runtimeType}.';
      return result;
    }
    final onFail = rawMap['on-fail'];
    if (onFail != null && onFail is! List) {
      result.enable = false;
      result.message =
          'exe-handle: "on-fail" must be a list, got: ${onFail.runtimeType}.';
      return result;
    }
    DataExeHandle data;
    try {
      data = DataExeHandle.fromJson(rawMap);
    } on TypeError catch (_) {
      result.enable = false;
      result.message = 'exe-handle: invalid field type in YAML definition.';
      return result;
    }
    var dataValidateResult = data.validate();
    if (!dataValidateResult.enable) {
      return dataValidateResult;
    }

    commandID = data.id!;
    if (!runtimeContext.dataCommandMap.containsKey(commandID)) {
      result.enable = false;
      result.message =
          'The "$commandID" command does not exist in the command list.';
    }
    updateState(CommandState.ready);

    handleData = ExeHandleData();
    //set success task
    var validateResult =
        PipelineExecutor.validateTasks(data.on_success, context: context);
    if (!validateResult.enable) {
      return validateResult;
    }
    handleData.pipelineSuccess = validateResult.pipeline;

    //set fail task
    validateResult =
        PipelineExecutor.validateTasks(data.on_fail, context: context);
    if (!validateResult.enable) {
      return validateResult;
    }
    handleData.pipelineFail = validateResult.pipeline;
    this.data = data;

    return result;
  }

  Future<DataCommand> get commandData async {
    var commandID = data.id;
    var commandData = runtimeContext.dataCommandMap[commandID]!;
    await Application.instance.printBuildStep(
        'Phase Start: ${commandData.name} ($commandID)', Color.green);
    return dataFutrue(commandData);
  }

  @override
  Future execute() async {
    updateState(CommandState.progress);
    var data = await commandData;
    var command = Command.byType(data.command);
    command.id = commandID;
    command.context = runtimeContext;
    await command.executeHandle(data, handleData);
    updateState(CommandState.complete);
    return simpleFuture;
  }

  void updateState(CommandState state) {
    runtimeContext.commandStates[commandID] = state;
  }
}
