import 'dart:async';

import '../domain/auth_service.dart';
import '../domain/auth_state.dart';
import '../domain/user.dart';

/// An in-memory [AuthService] implementation for use in `fake` mode and tests.
///
/// Accepts any non-empty email/password combination and returns a synthetic
/// [KcUser]. No network calls are made. State is held in memory and lost
/// when the process exits.
///
/// ## Fake credentials
///
/// | Email                        | Role     |
/// |------------------------------|----------|
/// | `admin@kellcreations.com`    | admin    |
/// | `operator@kellcreations.com` | operator |
/// | any other non-empty email    | viewer   |
///
/// Any non-empty password is accepted. An empty password throws [AuthException].
class FakeAuthService implements AuthService {
  final StreamController<AuthState> _controller = StreamController<AuthState>.broadcast();

  AuthState _state = const AuthStateUnauthenticated();

  @override
  Stream<AuthState> get authStateStream async* {
    // Emit current state immediately on subscription.
    yield _state;
    yield* _controller.stream;
  }

  @override
  AuthState get currentState => _state;

  @override
  KcUser? get currentUser => _state.user;

  @override
  Future<KcUser> signIn({required String email, required String password}) async {
    _emit(const AuthStateLoading());

    // Simulate a short async delay.
    await Future<void>.delayed(const Duration(milliseconds: 10));

    if (email.isEmpty) {
      final error = const AuthException(message: 'Email must not be empty.');
      _emit(AuthStateError(message: error.message));
      throw error;
    }

    if (password.isEmpty) {
      final error = const AuthException(message: 'Password must not be empty.');
      _emit(AuthStateError(message: error.message));
      throw error;
    }

    final role = _roleForEmail(email);
    final user = KcUser(
      id: 'fake-${email.hashCode.abs()}',
      displayName: _displayNameForEmail(email),
      email: email,
      role: role,
    );

    _emit(AuthStateAuthenticated(user: user));
    return user;
  }

  @override
  Future<void> signOut() async {
    _emit(const AuthStateLoading());
    await Future<void>.delayed(const Duration(milliseconds: 10));
    _emit(const AuthStateUnauthenticated());
  }

  @override
  Future<bool> refreshSession() async {
    // In fake mode the session never expires.
    return _state.isAuthenticated;
  }

  @override
  void dispose() {
    _controller.close();
  }

  void _emit(AuthState state) {
    _state = state;
    if (!_controller.isClosed) {
      _controller.add(state);
    }
  }

  KcUserRole _roleForEmail(String email) {
    if (email == 'admin@kellcreations.com') return KcUserRole.admin;
    if (email == 'operator@kellcreations.com') return KcUserRole.operator;
    return KcUserRole.viewer;
  }

  String _displayNameForEmail(String email) {
    if (email == 'admin@kellcreations.com') return 'Admin User';
    if (email == 'operator@kellcreations.com') return 'Operator User';
    // Derive a display name from the local part of the email.
    final local = email.split('@').first;
    return local.isNotEmpty ? local : email;
  }
}
