import 'package:moneymgr_mobile/services/api/api_client.dart';
import 'package:moneymgr_mobile/services/api/api_token.dart';
import 'package:moneymgr_mobile/services/api/auth_api.dart';
import 'package:moneymgr_mobile/services/storage/prefs.dart';
import 'package:moneymgr_mobile/services/storage/secure_storage.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../services/router/routes_list.dart';

part 'auth_state.g.dart';

/// The current authentication state of the app.
///
/// This notifier is responsible for saving/removing the token and profile info
/// to the storage through the [setAuthToken] and [logout] methods.
@riverpod
class CurrentAuthState extends _$CurrentAuthState {
  @override
  AuthState build() {
    final secureStorage = ref.watch(secureStorageProvider).requireValue;
    final token = secureStorage.token();
    return token != null ? AuthState.authenticated : AuthState.unauthenticated;
  }

  /// Attempts to authenticate with [token] and saves the token and profile info to storage.
  /// Will invalidate the state if success and throw an exception in case of failure
  Future<void> setAuthToken(ApiToken token) async {
    // Attempt to use provided token
    await ApiClient(
      token: token,
      prefs: await ref.watch(prefsProvider.future),
    ).authInfo();

    final secureStorage = ref.read(secureStorageProvider).requireValue;
    await secureStorage.setToken(token);

    ref
        // Invalidate the state so the auth state will be updated to authenticated.
        .invalidateSelf();
  }

  /// Logs out, deletes the saved token and profile info from storage, and invalidates
  /// the state.
  Future<void> logout() async {
    final prefs = ref.read(prefsProvider).requireValue;

    final secureStorage = ref.read(secureStorageProvider).requireValue;
    await secureStorage.removeToken();

    prefs.clearServerConfig();
    prefs.clearAuthInfo();

    ref
    // Invalidate the state so the auth state will be updated to authenticated.
        .invalidateSelf();
  }
}

/// The possible authentication states of the app.
enum AuthState {
  unknown(redirectPath: homePage, allowedPaths: [homePage]),
  unauthenticated(
    redirectPath: authPage,
    allowedPaths: [authPage, qrAuthPath, manualAuthPage, settingsPage],
  ),
  authenticated(
    redirectPath: homePage,
    allowedPaths: null,
    forbiddenPaths: [authPage, manualAuthPage],
  );

  const AuthState({
    required this.redirectPath,
    required this.allowedPaths,
    this.forbiddenPaths,
  });

  /// The target path to redirect when the current route is not allowed in this
  /// auth state.
  final String redirectPath;

  /// List of paths allowed when the app is in this auth state. May be set to null if there is no
  /// restriction applicable
  final List<String>? allowedPaths;

  /// List of paths not allowed when the app is in this auth state. May be set to null if there is no
  /// restriction applicable
  final List<String>? forbiddenPaths;
}
