///
/// AuthCredentialManager is a singleton class that manages the authentication process.
/// It is responsible for delegating the login process to the appropriate [AuthCredentialDelegate].
///
enum AuthType { apple, google, facebook, anonymous }

enum AuthCode { success, conflict, error }

String getAuthName(AuthType authType) {
  switch (authType) {
    case AuthType.apple:
      return "apple";
    case AuthType.google:
      return "google";
    case AuthType.facebook:
      return "facebook";
    default:
      return "anonymous";
  }
}

class AuthResult {
  final AuthCode code;
  final dynamic errorCause;
  final Credential? credential;

  bool get isSuccess => code == AuthCode.success;

  AuthResult.success(this.credential)
      : code = AuthCode.success,
        errorCause = null;

  AuthResult.error(this.errorCause)
      : code = AuthCode.error,
        credential = null;

  @override
  String toString() {
    return 'AuthResult{code: $code, credential: $credential, errorCause: $errorCause}';
  }
}

abstract class Credential {
  AuthType get authType;

  String get token;

  bool get isAnonymous => authType == AuthType.anonymous;

  bool get isFacebook => authType == AuthType.facebook;

  bool get isGoogle => authType == AuthType.google;

  bool get isApple => authType == AuthType.apple;
}

abstract class AuthCredentialDelegate {
  AuthType get authType;

  Future<AuthResult> login();

  Future<dynamic> logout();

  Credential deserializeCredential(String data);

  const AuthCredentialDelegate();
}

class AuthCredentialManager {
  AuthCredentialManager._(this._delegates);

  static late final AuthCredentialManager _instance;

  static AuthCredentialManager get instance => _instance;

  static void initialize(List<AuthCredentialDelegate> delegates) {
    _instance =
        AuthCredentialManager._({for (var delegate in delegates) delegate.authType: delegate});
  }

  final Map<AuthType, AuthCredentialDelegate> _delegates;

  List<AuthType> get supportedAuthType => _delegates.keys.toList();

  Future<AuthResult> loginWith(AuthType authType) async {
    final delegate = _delegates[authType];
    if (delegate == null) {
      return AuthResult.error("Delegate not found");
    }
    return delegate.login();
  }

  Future logout(AuthType authType) async {
    final delegate = _delegates[authType];
    if (delegate == null) {
      return AuthResult.error("Delegate not found");
    }
    return delegate.logout();
  }

  Credential? deserializeCredential(AuthType authType, String credentialData) {
    final delegate = _delegates[authType];
    if (delegate == null) {
      return null;
    }
    return delegate.deserializeCredential(credentialData);
  }
}
