abstract class AuthTokenStorageTarget {
  String? read(String key);
  void write(String key, String value);
  void remove(String key);
}

class AuthTokenStoreBackend {
  AuthTokenStoreBackend({
    required AuthTokenStorageTarget localTarget,
    required AuthTokenStorageTarget sessionTarget,
  }) : _targets = [localTarget, sessionTarget, _MemoryStorageTarget()];

  static const _tokenKey = 'baron_auth_token';
  static const _providerKey = 'baron_auth_provider';
  static const _cookieModeKey = 'baron_auth_cookie_mode';
  static const _pendingProviderKey = 'baron_auth_pending_provider';

  final List<AuthTokenStorageTarget> _targets;

  String? getToken() => _readFirst(_tokenKey);

  String? getProvider() => _readFirst(_providerKey);

  bool usesCookie() => _readFirst(_cookieModeKey) == '1';

  void setToken(String token, {String? provider}) {
    _writeAll(_tokenKey, token);
    _removeAll(_cookieModeKey);
    if (provider != null) {
      _writeAll(_providerKey, provider);
    }
  }

  void setCookieMode({String? provider}) {
    _writeAll(_cookieModeKey, '1');
    _removeAll(_tokenKey);
    if (provider != null) {
      _writeAll(_providerKey, provider);
    }
  }

  String? getPendingProvider() => _readFirst(_pendingProviderKey);

  void setPendingProvider(String? provider) {
    if (provider == null || provider.isEmpty) {
      _removeAll(_pendingProviderKey);
      return;
    }
    _writeAll(_pendingProviderKey, provider);
  }

  void clear() {
    _removeAll(_tokenKey);
    _removeAll(_providerKey);
    _removeAll(_cookieModeKey);
    _removeAll(_pendingProviderKey);
  }

  String? _readFirst(String key) {
    for (final target in _targets) {
      try {
        final value = target.read(key);
        if (value != null && value.isNotEmpty) {
          return value;
        }
      } catch (_) {
        continue;
      }
    }
    return null;
  }

  void _writeAll(String key, String value) {
    for (final target in _targets) {
      try {
        target.write(key, value);
      } catch (_) {
        continue;
      }
    }
  }

  void _removeAll(String key) {
    for (final target in _targets) {
      try {
        target.remove(key);
      } catch (_) {
        continue;
      }
    }
  }
}

class _MemoryStorageTarget implements AuthTokenStorageTarget {
  final Map<String, String> _memory = {};

  @override
  String? read(String key) => _memory[key];

  @override
  void remove(String key) {
    _memory.remove(key);
  }

  @override
  void write(String key, String value) {
    _memory[key] = value;
  }
}
