// ignore_for_file: depend_on_referenced_packages

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dart_framework/log/log.dart';
import 'package:dart_framework/platform/process.dart';
import 'package:oto/oto/application.dart';

abstract class CommandRuntime {
  Future<ProcessData> start(StringBuffer shell,
      {String? workspace,
      bool printStdout = true,
      bool printStderr = true,
      Converter<List<int>, String>? decoder,
      LogHandler? logHandler});

  Future<ProcessResult> run(StringBuffer shell,
      {String? workspace,
      bool printStdout = true,
      bool printStderr = true,
      Converter<List<int>, String>? decoder,
      LogHandler? logHandler});

  Future<ProcessResult> runExecutable(String exe, List<String> args,
      {String? workspace,
      bool? printStdout,
      bool? printStderr,
      LogHandler? logHandler});

  Future<void> startDetached(StringBuffer shell, {required String workspace});
}

class DefaultCommandRuntime implements CommandRuntime {
  const DefaultCommandRuntime();

  @override
  Future<ProcessData> start(StringBuffer shell,
      {String? workspace,
      bool printStdout = true,
      bool printStderr = true,
      Converter<List<int>, String>? decoder,
      LogHandler? logHandler}) {
    return ProcessExecutor.start(shell,
        workspace: workspace,
        printStdout: printStdout,
        printStderr: printStderr,
        decoder: decoder,
        logHandler: logHandler ?? Application.logWithType);
  }

  @override
  Future<ProcessResult> run(StringBuffer shell,
      {String? workspace,
      bool printStdout = true,
      bool printStderr = true,
      Converter<List<int>, String>? decoder,
      LogHandler? logHandler}) {
    return ProcessExecutor.run(shell,
        workspace: workspace,
        printStdout: printStdout,
        printStderr: printStderr,
        decoder: decoder,
        logHandler: logHandler);
  }

  @override
  Future<ProcessResult> runExecutable(String exe, List<String> args,
      {String? workspace,
      bool? printStdout,
      bool? printStderr,
      LogHandler? logHandler}) async {
    final result = await Process.run(exe, args, workingDirectory: workspace);
    final handler = logHandler ?? Application.logWithType;
    if (printStdout ?? true) {
      final out = result.stdout.toString();
      if (out.isNotEmpty) handler(out, LogType.verbose);
    }
    if (printStderr ?? true) {
      final err = result.stderr.toString();
      if (err.isNotEmpty) handler(err, LogType.error);
    }
    return result;
  }

  @override
  Future<void> startDetached(StringBuffer shell,
      {required String workspace}) async {
    final file =
        File('${Directory.systemTemp.path}/${ProcessExecutor.tempFileName}');
    await file.writeAsString(shell.toString());
    await Process.start(file.path, [],
        runInShell: true,
        workingDirectory: workspace,
        mode: ProcessStartMode.detached);
  }
}
