import 'dart:async';
import 'dart:io';

import 'package:equatable/equatable.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/data/core_repository.dart';
import 'package:flux/features/company/models/company_model.dart';
import 'package:flux/features/master_data/staff/models/staff_member_model.dart';
import 'package:flux/features/master_data/store/models/store_model.dart';
import 'package:get_it/get_it.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:collection/collection.dart'; // Per firstWhereOrNull

// Importa lo state con l'Enum e il CoreRepository...
part 'session_state.dart';

class SessionCubit extends Cubit<SessionState> {
  final CoreRepository _repository;
  final SharedPreferences _prefs; // Iniettato via GetIt
  final SupabaseClient _supabase = Supabase.instance.client;

  static const String _lastStoreKey = 'last_selected_store_id';

  SessionCubit(this._repository, this._prefs)
    : super(const SessionState(status: SessionStatus.initial)) {
    initializeSession();
    // Possiamo metterci in ascolto dei cambiamenti di Auth (Login/Logout)
    _supabase.auth.onAuthStateChange.listen((data) {
      final AuthChangeEvent event = data.event;
      if (event == AuthChangeEvent.signedIn) {
        initializeSession();
      } else if (event == AuthChangeEvent.signedOut) {
        emit(const SessionState(status: SessionStatus.unauthenticated));
      }
    });
  }

  Future<void> initializeSession() async {
    final user = _supabase.auth.currentUser;
    if (user == null) {
      return emit(state.copyWith(status: SessionStatus.unauthenticated));
    }

    try {
      // Riportiamo lo stato su initial per far girare lo spinner se stiamo riprovando
      emit(state.copyWith(status: SessionStatus.initial, errorMessage: null));

      // WRAP DELLA LOGICA IN UN BLOCCO PROTETTO DA TIMEOUT (10 Secondi)
      await Future(() async {
        StaffMemberModel? staff = await _repository.getStaffMemberByUserId(
          user.id,
        );
        CompanyModel? company;

        if (staff != null) {
          if (staff.hasJoined == false) {
            await _repository.updateStaffMember(staff.id!, {
              'has_joined': true,
            });
            staff = staff.copyWith(hasJoined: true);
          }
          company = await _repository.getCompanyById(staff.companyId);
        } else {
          company = await _repository.getCompanyByOwnerId(user.id);
        }

        if (company == null) {
          return emit(
            state.copyWith(
              status: SessionStatus.onboardingRequired,
              user: user,
              onboardingStep: OnboardingStep.company,
            ),
          );
        } else {
          emit(state.copyWith(company: company));
        }

        final stores = await _repository.getStoresByCompanyId(company.id!);
        if (stores.isEmpty) {
          return emit(
            state.copyWith(
              status: SessionStatus.onboardingRequired,
              user: user,
              company: company,
              onboardingStep: OnboardingStep.store,
            ),
          );
        } else {
          emit(state.copyWith(currentStore: stores.first));
        }

        if (staff == null) {
          return emit(
            state.copyWith(
              status: SessionStatus.onboardingRequired,
              user: user,
              company: company,
              onboardingStep: OnboardingStep.staff,
            ),
          );
        }

        final lastStoreId = _prefs.getString(_lastStoreKey);
        final activeStore =
            stores.firstWhereOrNull((s) => s.id == lastStoreId) ?? stores.first;

        if (lastStoreId != activeStore.id && activeStore.id != null) {
          await _prefs.setString(_lastStoreKey, activeStore.id!);
        }

        setIsSingleUserMode(_prefs.getBool('isSingleUserMode') ?? false);

        emit(
          state.copyWith(
            status: SessionStatus.authenticated,
            user: user,
            company: company,
            currentStore: activeStore,
            currentStaffMember: staff,
            onboardingStep: OnboardingStep.none,
          ),
        );

        // FCM è fuori dall'await principale, quindi va bene così
        _registerFcmToken(companyId: company.id!, staffId: staff.id!);
      }).timeout(
        const Duration(seconds: 10), // Tempo massimo concesso al server
        onTimeout: () {
          throw TimeoutException(
            'Il server di FLUX non risponde. Controlla la connessione.',
          );
        },
      );
    } on TimeoutException catch (e) {
      // 🎯 BINGO! IL TIMEOUT È SCATTATO
      debugPrint("Timeout Inizializzazione: ${e.message}");
      emit(
        state.copyWith(status: SessionStatus.error, errorMessage: e.message),
      );
    } catch (e) {
      // Altri errori generici del DB o di rete
      debugPrint("Errore Inizializzazione: $e");
      emit(
        state.copyWith(
          status: SessionStatus.error,
          errorMessage: "Si è verificato un errore di connessione imprevisto.",
        ),
      );
    }
  }

  Future<void> _registerFcmToken({
    required String companyId,
    required String staffId,
  }) async {
    // Scudo anti-crash per lo sviluppo su Linux / Windows
    if (!kIsWeb &&
        !Platform.isAndroid &&
        !Platform.isIOS &&
        !Platform.isMacOS) {
      return;
    }

    try {
      final messaging = FirebaseMessaging.instance;

      // 1. Richiesta permessi di notifica
      final settings = await messaging.requestPermission(
        alert: true,
        badge: true,
        sound: true,
      );

      if (settings.authorizationStatus == AuthorizationStatus.authorized) {
        String? fcmToken;
        if (kIsWeb) {
          fcmToken = await messaging.getToken(
            vapidKey:
                'BLMUr7crlRghEW6iWtRZ7Y0a74OPAMG9Oh37ewhVP3_5YD9e5RHUeO79sDys6P-7KjOz6I6HiaPqNndmatQlu3g',
          );
        } else {
          fcmToken = await messaging.getToken();
        }

        if (fcmToken != null) {
          final supabase = GetIt.I.get<SupabaseClient>();

          // Determiniamo la piattaforma in modo sicuro per Linux
          String osPlatform = 'web';
          if (!kIsWeb) {
            if (Platform.isAndroid) osPlatform = 'android';
            if (Platform.isIOS) osPlatform = 'ios';
            if (Platform.isMacOS) osPlatform = 'macos';
          }

          // 3. UPSERT su Supabase condizionato dal vincolo 'fcm_token'
          await supabase.from('staff_devices').upsert(
            {
              'company_id': companyId,
              'staff_id': staffId,
              'fcm_token': fcmToken,
              'os_platform': osPlatform,
              'updated_at': DateTime.now().toIso8601String(),
            },
            onConflict:
                'fcm_token', // Se il token esiste già, aggiorna questa riga!
          );

          debugPrint(
            'Dispositivo registrato con successo su FLUX Cloud. Platform: $osPlatform',
          );
        }
      } else {
        debugPrint('Permesso push negato dall\'utente.');
      }
    } catch (e) {
      debugPrint('Errore durante la registrazione del dispositivo: $e');
    }
  }

  void updateCurrentCompany(CompanyModel newCompany) {
    emit(state.copyWith(company: newCompany));
  }

  // --- FUNZIONE EXTRA: CAMBIO NEGOZIO DALLA DASHBOARD ---
  Future<void> changeStore(StoreModel newStore) async {
    if (newStore.id != null) {
      await _prefs.setString(_lastStoreKey, newStore.id!);
      emit(state.copyWith(currentStore: newStore));
    }
  }

  // --- LOGOUT ---
  Future<void> signOut() async {
    await _supabase.auth.signOut();
    // Non serve emettere stato qui, ci pensa il listener nel costruttore!
  }

  void setIsMobileDevice(bool isMobile) {
    emit(state.copyWith(isMobileDevice: isMobile));
  }

  void setIsSingleUserMode(bool isSingleUser) {
    emit(state.copyWith(isSingleUserMode: isSingleUser));
  }

  void updateCurrentStoreLocally(StoreModel updatedStore) {
    // Verifichiamo che l'utente stia effettivamente lavorando nel negozio appena modificato
    if (state.currentStore != null &&
        state.currentStore!.id == updatedStore.id) {
      // Emettiamo il nuovo stato sovrascrivendo solo il negozio corrente
      emit(state.copyWith(currentStore: updatedStore));
    }
  }
}
