export class Result<T, E = Error> {
  private constructor(
    private readonly _value?: T,
    private readonly _error?: E,
    private readonly _isSuccess: boolean = true
  ) {}

  static ok<T, E = Error>(value: T): Result<T, E> {
    return new Result<T, E>(value, undefined, true);
  }

  static err<T, E = Error>(error: E): Result<T, E> {
    return new Result<T, E>(undefined, error, false);
  }

  isOk(): boolean {
    return this._isSuccess;
  }

  isErr(): boolean {
    return !this._isSuccess;
  }

  unwrap(): T {
    if (!this._isSuccess) {
      throw new Error('Called unwrap on an error result');
    }
    return this._value!;
  }

  unwrapOr(defaultValue: T): T {
    return this._isSuccess ? this._value! : defaultValue;
  }

  unwrapErr(): E {
    if (this._isSuccess) {
      throw new Error('Called unwrapErr on a success result');
    }
    return this._error!;
  }

  map<U>(fn: (value: T) => U): Result<U, E> {
    if (this._isSuccess) {
      return Result.ok(fn(this._value!));
    }
    return Result.err(this._error!);
  }

  mapErr<F>(fn: (error: E) => F): Result<T, F> {
    if (!this._isSuccess) {
      return Result.err(fn(this._error!));
    }
    return Result.ok(this._value!);
  }

  andThen<U>(fn: (value: T) => Result<U, E>): Result<U, E> {
    if (this._isSuccess) {
      return fn(this._value!);
    }
    return Result.err(this._error!);
  }
}