import 'package:logging/logging.dart';
import 'package:result_monad/result_monad.dart';
import 'package:stack_trace/stack_trace.dart';

Result<T, ExecError> buildErrorResult<T>({
  required ErrorType type,
  String message = '',
}) =>
    Result.error(
      ExecError(
        type: type,
        message: message,
      ),
    );

class ExecError {
  final ErrorType type;
  final String message;
  final Trace trace;

  ExecError({required this.type, this.message = '', Trace? trace})
      : trace = trace ?? Trace.current(1);

  ExecError copy({
    ErrorType? type,
    String? message,
    Trace? trace,
  }) =>
      ExecError(
        type: type ?? this.type,
        message: message ?? this.message,
        trace: trace ?? this.trace,
      );

  @override
  String toString() {
    return 'ExecError{type: $type, message: $message}';
  }

  String printStackTrace() => trace.terse.toString();
}

enum ErrorType {
  authentication,
  localError,
  missingEndpoint,
  minVersionError,
  notFound,
  parsingError,
  serverError,
  rangeError,
}

extension ExecErrorExtension<T, E> on Result<T, E> {
  Result<T, ExecError> execErrorCast() => mapError((error) => error is ExecError
      ? error
      : ExecError(type: ErrorType.localError, message: error.toString()));

  FutureResult<T, ExecError> execErrorCastAsync() async {
    return this.execErrorCast();
  }
}

extension ExecErrorExtensionFuture<T, E> on FutureResult<T, E> {
  FutureResult<T, ExecError> execErrorCastAsync() async =>
      (await this).execErrorCast();
}

void logError(ExecError error, Logger logger,
    {Level logLevel = Level.INFO, String message = ''}) {
  logger.log(logLevel, '$message $error\n${error.trace}');
}
