import 'user.dart';

/// Represents the current authentication state of the application.
///
/// [AuthState] is a sealed-style class hierarchy using a discriminated union
/// pattern. The app always holds exactly one of:
///
/// - [AuthStateUnauthenticated] — no user is signed in
/// - [AuthStateLoading] — an auth operation is in progress
/// - [AuthStateAuthenticated] — a user is signed in
/// - [AuthStateError] — an auth operation failed
///
/// Use [AuthState.isAuthenticated], [AuthState.user], and the `when`-style
/// helpers to branch on state without casting.
sealed class AuthState {
  const AuthState();

  /// Returns `true` if the user is currently authenticated.
  bool get isAuthenticated => this is AuthStateAuthenticated;

  /// Returns `true` if an auth operation is in progress.
  bool get isLoading => this is AuthStateLoading;

  /// Returns the authenticated [KcUser], or `null` if not authenticated.
  KcUser? get user => switch (this) {
    AuthStateAuthenticated(:final user) => user,
    _ => null,
  };
}

/// No user is currently signed in.
final class AuthStateUnauthenticated extends AuthState {
  const AuthStateUnauthenticated();

  @override
  String toString() => 'AuthState.unauthenticated';
}

/// An authentication operation (login, logout, token refresh) is in progress.
final class AuthStateLoading extends AuthState {
  const AuthStateLoading();

  @override
  String toString() => 'AuthState.loading';
}

/// A user is signed in and the session is valid.
final class AuthStateAuthenticated extends AuthState {
  /// The currently authenticated user.
  @override
  final KcUser user;

  const AuthStateAuthenticated({required this.user});

  @override
  bool operator ==(Object other) =>
      identical(this, other) || other is AuthStateAuthenticated && user == other.user;

  @override
  int get hashCode => user.hashCode;

  @override
  String toString() => 'AuthState.authenticated(user: $user)';
}

/// An authentication operation failed.
final class AuthStateError extends AuthState {
  /// A human-readable description of what went wrong.
  final String message;

  /// The underlying exception, if available.
  final Object? cause;

  const AuthStateError({required this.message, this.cause});

  @override
  bool operator ==(Object other) =>
      identical(this, other) || other is AuthStateError && message == other.message;

  @override
  int get hashCode => message.hashCode;

  @override
  String toString() => 'AuthState.error(message: $message)';
}
