import 'auth_state.dart';
import 'user.dart';

/// Abstract contract for authentication operations.
///
/// Implementations handle login, logout, session persistence, and token
/// refresh. The concrete implementation is selected at runtime — a
/// [FakeAuthService] is used in `fake` mode; a real implementation (e.g.
/// local credential validation or an OAuth flow) is used in production.
///
/// The service exposes the current [AuthState] as a [Stream] so that widgets
/// and controllers can reactively rebuild when auth state changes.
abstract class AuthService {
  /// A stream that emits the current [AuthState] whenever it changes.
  ///
  /// The stream always emits the current state immediately upon subscription
  /// (i.e. it behaves like a `BehaviorSubject`).
  Stream<AuthState> get authStateStream;

  /// The current [AuthState] synchronously.
  AuthState get currentState;

  /// The currently authenticated [KcUser], or `null` if not signed in.
  KcUser? get currentUser;

  /// Attempts to sign in with the given [email] and [password].
  ///
  /// Returns the authenticated [KcUser] on success.
  /// Throws [AuthException] on failure (wrong credentials, network error, etc.).
  Future<KcUser> signIn({required String email, required String password});

  /// Signs out the current user and clears any persisted session.
  Future<void> signOut();

  /// Attempts to refresh the current session token.
  ///
  /// Returns `true` if the session was successfully refreshed.
  /// Returns `false` if the session has expired and the user must sign in again.
  Future<bool> refreshSession();

  /// Releases resources held by this service (stream controllers, timers).
  void dispose();
}

/// Thrown when an authentication operation fails.
class AuthException implements Exception {
  /// A human-readable description of the failure.
  final String message;

  /// The HTTP status code, if the failure originated from an API call.
  final int? statusCode;

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

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

  @override
  String toString() =>
      'AuthException: $message'
      '${statusCode != null ? ' (HTTP $statusCode)' : ''}';
}
