// // Copyright (C) 2022 WYATT GROUP
// // Please see the AUTHORS file for details.
// //
// // This program is free software: you can redistribute it and/or modify
// // it under the terms of the GNU General Public License as published by
// // the Free Software Foundation, either version 3 of the License, or
// // any later version.
// //
// // This program is distributed in the hope that it will be useful,
// // but WITHOUT ANY WARRANTY; without even the implied warranty of
// // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// // GNU General Public License for more details.
// //
// // You should have received a copy of the GNU General Public License
// // along with this program. If not, see <https://www.gnu.org/licenses/>.

// // ignore_for_file: avoid_positional_boolean_parameters

import 'dart:async';

part 'future_result.dart';
part 'future_or_result.dart';
part 'result.dart';
part 'option.dart';

mixin _Left<LeftType, RightType> on _EitherBase<LeftType, RightType> {}

mixin _Right<LeftType, RightType> on _EitherBase<LeftType, RightType> {}

class _EitherBaseException implements Exception {
  const _EitherBaseException(this.message);
  final String message;

  @override
  String toString() => '_EitherException: $message';
}

abstract class _EitherBase<LeftType, RightType> {
  const _EitherBase();

  bool get _isLeft => this is _Left<LeftType, RightType>;

  bool get _isRight => this is _Right<LeftType, RightType>;

  LeftType? get _left => _fold<LeftType?>(
        (value) => value,
        (right) => null,
      );

  RightType? get _right => _fold<RightType?>(
        (left) => null,
        (value) => value,
      );

  U _unwrap<U>() {
    if (U == LeftType) {
      return _fold<LeftType>(
        (value) => value,
        (right) => throw const ResultException(
          'Illegal use. You should check left value before calling',
        ),
      ) as U;
    }
    if (U == RightType) {
      return _fold<RightType>(
        (left) => throw const ResultException(
          'Illegal use. You should check right value before calling',
        ),
        (value) => value,
      ) as U;
    }
    throw ResultException(
      'Illegal use. You should use $LeftType or $RightType type',
    );
  }

  Future<U> _unwrapAsync<U>() {
    if (U == LeftType) {
      return _foldAsync<LeftType>(
        Future.value,
        (right) => throw const ResultException(
          'Illegal use. You should check left value before calling',
        ),
      ) as Future<U>;
    }
    if (U == RightType) {
      return _foldAsync<RightType>(
        (left) => throw const ResultException(
          'Illegal use. You should check right value before calling',
        ),
        Future.value,
      ) as Future<U>;
    }
    throw ResultException(
      'Illegal use. You should use $LeftType or $RightType type',
    );
  }

  U _fold<U>(
    U Function(LeftType left) fnL,
    U Function(RightType right) fnR,
  );

  Future<U> _foldAsync<U>(
    Future<U> Function(LeftType left) fnL,
    Future<U> Function(RightType right) fnR,
  );

  _EitherBase<U, RightType> _and<U>(
    _EitherBase<U, RightType> res,
  );

  _EitherBase<LeftType, F> _or<F>(
    _EitherBase<LeftType, F> res,
  );

  bool _contains<U>(U x);

  LeftType _expect(String msg);

  RightType _expectErr(String msg);

  _EitherBase<U, RightType> _map<U>(
    U Function(LeftType left) mapper,
  );

  Future<_EitherBase<U, RightType>> _mapAsync<U>(
    Future<U> Function(LeftType left) mapper,
  );

  _EitherBase<LeftType, F> _mapErr<F>(
    F Function(RightType right) mapper,
  );

  Future<_EitherBase<LeftType, F>> _mapErrAsync<F>(
    Future<F> Function(RightType right) mapper,
  );

  _EitherBase<L, R> _either<L, R>(
    L Function(LeftType left) fnL,
    R Function(RightType right) fnR,
  );

  Future<_EitherBase<L, R>> _eitherAsync<L, R>(
    Future<L> Function(LeftType left) fnL,
    Future<R> Function(RightType right) fnR,
  );
}
