import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:injectable/injectable.dart';
import '../../data/models/user.dart';
import '../../data/models/user_role.dart';
import '../../data/datasources/keycloak_auth_service.dart';
import '../../data/datasources/permission_engine.dart';
import '../../../../core/config/environment.dart';
import '../../../../core/network/org_context_service.dart';
import '../../../../core/storage/dashboard_cache_manager.dart';
import '../../../../core/utils/logger.dart';
import '../../../../core/di/injection.dart';
import '../../../organizations/domain/repositories/organization_repository.dart';

// === ÉVÉNEMENTS ===
abstract class AuthEvent extends Equatable {
  const AuthEvent();
  @override
  List<Object?> get props => [];
}

class AuthLoginRequested extends AuthEvent {
  const AuthLoginRequested();
}

class AuthLogoutRequested extends AuthEvent { const AuthLogoutRequested(); }
class AuthStatusChecked extends AuthEvent { const AuthStatusChecked(); }
class AuthTokenRefreshRequested extends AuthEvent { const AuthTokenRefreshRequested(); }

// === ÉTATS ===
abstract class AuthState extends Equatable {
  const AuthState();
  @override
  List<Object?> get props => [];
}

class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthUnauthenticated extends AuthState {}

class AuthAuthenticated extends AuthState {
  final User user;
  final UserRole effectiveRole;
  final List<String> effectivePermissions;
  final String accessToken;

  const AuthAuthenticated({
    required this.user,
    required this.effectiveRole,
    required this.effectivePermissions,
    required this.accessToken,
  });

  @override
  List<Object?> get props => [user, effectiveRole, effectivePermissions, accessToken];
}

class AuthError extends AuthState {
  final String message;
  const AuthError(this.message);
  @override
  List<Object?> get props => [message];
}

/// Compte bloqué (SUSPENDU ou DESACTIVE) — déconnexion + message.
class AuthAccountNotActive extends AuthState {
  final String statutCompte;
  final String message;
  const AuthAccountNotActive({required this.statutCompte, required this.message});
  @override
  List<Object?> get props => [statutCompte, message];
}

/// Compte EN_ATTENTE_VALIDATION — l'OrgAdmin doit compléter l'onboarding.
/// On ne déconnecte PAS pour permettre les appels API de souscription.
class AuthPendingOnboarding extends AuthState {
  final String onboardingState; // NO_SUBSCRIPTION | AWAITING_PAYMENT | PAYMENT_INITIATED | AWAITING_VALIDATION | VALIDATED
  final String? souscriptionId;
  final String? organisationId;
  final String? typeOrganisation;
  const AuthPendingOnboarding({
    required this.onboardingState,
    this.souscriptionId,
    this.organisationId,
    this.typeOrganisation,
  });
  @override
  List<Object?> get props => [onboardingState, souscriptionId, organisationId, typeOrganisation];
}

// Nouvel événement : auto-select l'org active après login (pour membres mono-org)
class AuthOrgContextInitRequested extends AuthEvent {
  final String organisationId;
  final String organisationNom;
  final String? type;
  const AuthOrgContextInitRequested({
    required this.organisationId,
    required this.organisationNom,
    this.type,
  });
  @override
  List<Object?> get props => [organisationId, organisationNom, type];
}

// === BLOC ===
@lazySingleton
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final KeycloakAuthService _authService;
  final OrgContextService _orgContextService;

  AuthBloc(this._authService, this._orgContextService) : super(AuthInitial()) {
    on<AuthLoginRequested>(_onLoginRequested);
    on<AuthLogoutRequested>(_onLogoutRequested);
    on<AuthStatusChecked>(_onStatusChecked);
    on<AuthTokenRefreshRequested>(_onTokenRefreshRequested);
    on<AuthOrgContextInitRequested>(_onOrgContextInit);
  }

  Future<void> _onLoginRequested(AuthLoginRequested event, Emitter<AuthState> emit) async {
    emit(AuthLoading());
    try {
      var rawUser = await _authService.loginWithAppAuth();
      if (rawUser != null) {
        // Vérification du statut du compte UnionFlow (indépendant de Keycloak)
        var status = await _authService.getAuthStatus(AppConfig.apiBaseUrl);

        // Ancien compte détecté : UPDATE_PASSWORD vient d'être assigné dans Keycloak.
        // Déclencher une nouvelle authentification AppAuth pour afficher l'écran de changement.
        if (status != null && status.reAuthRequired) {
          AppLogger.info('AuthBloc: réauthentification requise (ancien compte), re-déclenchement AppAuth');
          await _authService.logout();
          final reAuthUser = await _authService.loginWithAppAuth();
          if (reAuthUser == null) {
            emit(const AuthError('Connexion annulée.'));
            return;
          }
          rawUser = reAuthUser;
          status = await _authService.getAuthStatus(AppConfig.apiBaseUrl);
          // Garde-fou : si toujours reAuthRequired après la seconde tentative → erreur de config
          if (status != null && status.reAuthRequired) {
            AppLogger.error('AuthBloc: reAuthRequired persistant après réauthentification');
            emit(const AuthError('Erreur de configuration du compte. Contactez votre administrateur.'));
            return;
          }
        }

        if (status != null && status.isPendingOnboarding) {
          // Souscription VALIDATED = paiement confirmé, activation imminente.
          // Rafraîchir le token : si le backend a déjà activé le membre,
          // les rôles MEMBRE/MEMBRE_ACTIF seront dans le nouveau token → dashboard.
          if (status.onboardingState == 'VALIDATED') {
            await _authService.refreshToken();
            final refreshedStatus = await _authService.getAuthStatus(AppConfig.apiBaseUrl);
            if (refreshedStatus != null && refreshedStatus.isActive) {
              // Activation effective → procéder au dashboard
              final refreshedRawUser = await _authService.getCurrentUser();
              final user = await _enrichUserWithOrgContext(refreshedRawUser ?? rawUser);
              final permissions = await PermissionEngine.getEffectivePermissions(user);
              final token = await _authService.getValidToken();
              await DashboardCacheManager.invalidateForRole(user.primaryRole);
              emit(AuthAuthenticated(
                user: user,
                effectiveRole: user.primaryRole,
                effectivePermissions: permissions,
                accessToken: token ?? '',
              ));
              return;
            }
            // Activation pas encore effective (edge case) → afficher page d'attente avec polling
          }
          // OrgAdmin en attente → rediriger vers l'onboarding (sans déconnecter)
          final user = await _enrichUserWithOrgContext(rawUser);
          final orgId = status.organisationId ??
              (user.organizationContexts.isNotEmpty
                  ? user.organizationContexts.first.organizationId
                  : null);
          emit(AuthPendingOnboarding(
            onboardingState: status.onboardingState,
            souscriptionId: status.souscriptionId,
            organisationId: orgId,
            typeOrganisation: status.typeOrganisation,
          ));
          return;
        }

        if (status != null && status.isBlocked) {
          await _authService.logout();
          emit(AuthAccountNotActive(
            statutCompte: status.statutCompte,
            message: _messageForStatut(status.statutCompte),
          ));
          return;
        }

        // Si premier login venant d'être complété, rafraîchir le token pour obtenir
        // les rôles MEMBRE + MEMBRE_ACTIF assignés par le backend lors de l'activation.
        User user;
        if (status != null && status.premierLoginComplet) {
          await _authService.refreshToken();
          final refreshedRawUser = await _authService.getCurrentUser();
          user = await _enrichUserWithOrgContext(refreshedRawUser ?? rawUser);
        } else {
          user = await _enrichUserWithOrgContext(rawUser);
        }

        final permissions = await PermissionEngine.getEffectivePermissions(user);
        final token = await _authService.getValidToken();
        await DashboardCacheManager.invalidateForRole(user.primaryRole);

        emit(AuthAuthenticated(
          user: user,
          effectiveRole: user.primaryRole,
          effectivePermissions: permissions,
          accessToken: token ?? '',
        ));
      } else {
        emit(const AuthError('Identifiants incorrects.'));
      }
    } catch (e) {
      emit(AuthError('Erreur de connexion: $e'));
    }
  }

  Future<void> _onLogoutRequested(AuthLogoutRequested event, Emitter<AuthState> emit) async {
    emit(AuthLoading());
    await _authService.logout();
    await DashboardCacheManager.clear();
    _orgContextService.clear();
    emit(AuthUnauthenticated());
  }

  Future<void> _onOrgContextInit(
    AuthOrgContextInitRequested event,
    Emitter<AuthState> emit,
  ) async {
    _orgContextService.setActiveOrganisation(
      organisationId: event.organisationId,
      nom: event.organisationNom,
      type: event.type,
    );
    AppLogger.info('AuthBloc: contexte org initialisé → ${event.organisationNom}');
  }

  Future<void> _onStatusChecked(AuthStatusChecked event, Emitter<AuthState> emit) async {
    final tokenValid = await _authService.getValidToken();
    final isAuth = tokenValid != null;
    if (!isAuth) {
      emit(AuthUnauthenticated());
      return;
    }
    final rawUser = await _authService.getCurrentUser();
    if (rawUser == null) {
      emit(AuthUnauthenticated());
      return;
    }

    // Vérification du statut du compte (au redémarrage de l'app)
    final status = await _authService.getAuthStatus(AppConfig.apiBaseUrl);

    // Ancien compte sans UPDATE_PASSWORD : effacer la session locale et renvoyer vers le login.
    // L'utilisateur sera invité à se reconnecter — Keycloak affichera l'écran de changement de mot de passe.
    if (status != null && status.reAuthRequired) {
      AppLogger.info('AuthBloc: réauthentification requise au démarrage (ancien compte)');
      await _authService.logout();
      emit(AuthUnauthenticated());
      return;
    }

    if (status != null && status.isPendingOnboarding) {
      // Souscription VALIDATED : vérifier si le backend a finalisé l'activation
      if (status.onboardingState == 'VALIDATED') {
        await _authService.refreshToken();
        final refreshedStatus = await _authService.getAuthStatus(AppConfig.apiBaseUrl);
        if (refreshedStatus != null && refreshedStatus.isActive) {
          final refreshedRawUser = await _authService.getCurrentUser();
          final userActive = await _enrichUserWithOrgContext(refreshedRawUser ?? rawUser);
          final permissions = await PermissionEngine.getEffectivePermissions(userActive);
          final token = await _authService.getValidToken();
          await DashboardCacheManager.invalidateForRole(userActive.primaryRole);
          emit(AuthAuthenticated(
            user: userActive,
            effectiveRole: userActive.primaryRole,
            effectivePermissions: permissions,
            accessToken: token ?? '',
          ));
          return;
        }
        // Activation pas encore effective → continuer vers la page d'attente
      }
      final user = await _enrichUserWithOrgContext(rawUser);
      final orgId = status.organisationId ??
          (user.organizationContexts.isNotEmpty
              ? user.organizationContexts.first.organizationId
              : null);
      emit(AuthPendingOnboarding(
        onboardingState: status.onboardingState,
        souscriptionId: status.souscriptionId,
        organisationId: orgId,
        typeOrganisation: status.typeOrganisation ?? 'ASSOCIATION',
      ));
      return;
    }

    if (status != null && status.isBlocked) {
      await _authService.logout();
      emit(AuthAccountNotActive(
        statutCompte: status.statutCompte,
        message: _messageForStatut(status.statutCompte),
      ));
      return;
    }

    // Si premier login venant d'être complété, rafraîchir le token pour obtenir
    // les rôles MEMBRE + MEMBRE_ACTIF assignés par le backend lors de l'activation.
    User user;
    if (status != null && status.premierLoginComplet) {
      await _authService.refreshToken();
      final refreshedRawUser = await _authService.getCurrentUser();
      user = await _enrichUserWithOrgContext(refreshedRawUser ?? rawUser);
    } else {
      user = await _enrichUserWithOrgContext(rawUser);
    }

    final permissions = await PermissionEngine.getEffectivePermissions(user);
    final token = await _authService.getValidToken();
    emit(AuthAuthenticated(
      user: user,
      effectiveRole: user.primaryRole,
      effectivePermissions: permissions,
      accessToken: token ?? '',
    ));
  }

  /// Retourne un message lisible selon le statut du compte.
  String _messageForStatut(String statut) {
    switch (statut) {
      case 'EN_ATTENTE_VALIDATION':
        return 'Votre compte est en attente de validation par un administrateur. Vous serez notifié dès que votre accès sera activé.';
      case 'SUSPENDU':
        return 'Votre compte a été suspendu temporairement. Contactez votre administrateur pour plus d\'informations.';
      case 'DESACTIVE':
        return 'Votre compte a été désactivé. Contactez votre administrateur.';
      default:
        return 'Votre compte n\'est pas encore actif. Contactez votre administrateur.';
    }
  }

  /// Enrichit le contexte organisationnel pour les AdminOrganisation.
  ///
  /// Si le rôle est [UserRole.orgAdmin] et que [organizationContexts] est vide,
  /// appelle GET /api/organisations/mes pour récupérer les organisations de l'admin.
  /// Auto-initialise [OrgContextService] si une seule organisation.
  Future<User> _enrichUserWithOrgContext(User user) async {
    if (user.primaryRole != UserRole.orgAdmin ||
        user.organizationContexts.isNotEmpty) {
      // Auto-select le premier contexte existant si pas encore de contexte actif
      if (!_orgContextService.hasContext && user.organizationContexts.isNotEmpty) {
        final first = user.organizationContexts.first;
        _orgContextService.setActiveOrganisation(
          organisationId: first.organizationId,
          nom: first.organizationName,
        );
      }
      return user;
    }
    try {
      final orgRepo = getIt<IOrganizationRepository>();
      final orgs = await orgRepo.getMesOrganisations();
      if (orgs.isEmpty) return user;
      final contexts = orgs
          .where((o) => o.id != null && o.id!.isNotEmpty)
          .map(
            (o) => UserOrganizationContext(
              organizationId: o.id!,
              organizationName: o.nom,
              role: UserRole.orgAdmin,
              joinedAt: DateTime.now(),
            ),
          )
          .toList();
      if (contexts.isEmpty) return user;
      // Auto-select si une seule organisation
      if (contexts.length == 1 && !_orgContextService.hasContext) {
        _orgContextService.setActiveOrganisation(
          organisationId: contexts.first.organizationId,
          nom: contexts.first.organizationName,
        );
      }
      return user.copyWith(organizationContexts: contexts);
    } catch (e) {
      AppLogger.warning('AuthBloc: impossible de charger le contexte org: $e');
      return user;
    }
  }

  Future<void> _onTokenRefreshRequested(AuthTokenRefreshRequested event, Emitter<AuthState> emit) async {
    if (state is AuthAuthenticated) {
      final newToken = await _authService.refreshToken();
      final success = newToken != null;
      if (success) {
        add(AuthStatusChecked());
      } else {
        add(AuthLogoutRequested());
      }
    }
  }
}
