import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

class SessionManager {
  static const String _lastActiveTimeKey = 'last_active_time';
  static const String _sessionStateKey = 'session_state';
  static const int _sessionTimeoutMinutes = 15;

  static Timer? _sessionCheckTimer;
  static bool _isCheckingSession = false;
  static bool _isAppInBackground = false;
  static bool _isSessionExpired = false;
  static final StreamController<bool> _sessionExpiredController =
      StreamController<bool>.broadcast();

  // Stream untuk mendengarkan perubahan status session
  static Stream<bool> get sessionExpiredStream =>
      _sessionExpiredController.stream;

  // Initialize session management
  static Future<void> initializeSession() async {
    try {
      // Check if user is authenticated first
      final currentUser = Supabase.instance.client.auth.currentUser;
      if (currentUser == null) {
        debugPrint('Session: No authenticated user found');
        _setSessionExpired(true);
        return;
      }

      await updateLastActiveTime();
      _isAppInBackground = false;
      _setSessionExpired(false);

      debugPrint(
        'Session: Initialized successfully for user: ${currentUser.email}',
      );
    } catch (e) {
      debugPrint('Session: Error initializing - $e');
      _setSessionExpired(true);
    }
  }

  // Update last active time with better error handling
  static Future<void> updateLastActiveTime() async {
    try {
      // Only update if user is authenticated
      final currentUser = Supabase.instance.client.auth.currentUser;
      if (currentUser == null) {
        debugPrint('Session: Cannot update activity - user not authenticated');
        return;
      }

      final prefs = await SharedPreferences.getInstance();
      final now = DateTime.now().millisecondsSinceEpoch;
      await prefs.setInt(_lastActiveTimeKey, now);

      // Store session state
      await prefs.setString(_sessionStateKey, 'active');

      debugPrint(
        'Session: Activity updated at ${DateTime.fromMillisecondsSinceEpoch(now)}',
      );
    } catch (e) {
      debugPrint('Session: Error updating activity - $e');
    }
  }

  // Called when app goes to background
  static Future<void> onAppBackground() async {
    debugPrint('Session: App entering background');
    _isAppInBackground = true;
    await updateLastActiveTime();
    _startSessionMonitoring();
  }

  // Called when app comes to foreground
  static Future<void> onAppForeground() async {
    debugPrint('Session: App entering foreground');
    if (!_isAppInBackground) return; // Skip if already in foreground

    _isAppInBackground = false;

    try {
      // First check if user is authenticated
      final currentUser = Supabase.instance.client.auth.currentUser;
      if (currentUser == null) {
        debugPrint(
          'Session: No authenticated user when returning to foreground',
        );
        _stopSessionMonitoring();
        return;
      }

      final isValid = await isSessionValid();
      if (!isValid) {
        debugPrint('Session: Expired while in background');
        await clearSession();
        // Notify UI that session has expired with a slight delay to ensure app is ready
        Future.delayed(const Duration(milliseconds: 500), () {
          _setSessionExpired(true);
        });
      } else {
        debugPrint('Session: Still valid after background');
        await updateLastActiveTime();
      }
    } catch (e) {
      debugPrint('Session: Error during foreground transition - $e');
    } finally {
      _stopSessionMonitoring(); // Always stop background monitoring
    }
  }

  // Check if session is valid with improved logic
  static Future<bool> isSessionValid() async {
    try {
      // First check if user is authenticated via Supabase
      final currentUser = Supabase.instance.client.auth.currentUser;
      final currentSession = Supabase.instance.client.auth.currentSession;

      if (currentUser == null || currentSession == null) {
        debugPrint('Session: No valid Supabase session found');
        // Don't trigger session expired notification for unauthenticated users
        return false;
      }

      // Check if session token is expired
      final sessionExpiry = currentSession.expiresAt;
      if (sessionExpiry != null &&
          sessionExpiry <= DateTime.now().millisecondsSinceEpoch ~/ 1000) {
        debugPrint('Session: Supabase session token expired');
        _setSessionExpired(true);
        return false;
      }

      // Check our custom activity timeout
      final prefs = await SharedPreferences.getInstance();
      final lastActiveTime = prefs.getInt(_lastActiveTimeKey);

      if (lastActiveTime == null) {
        debugPrint('Session: No activity timestamp found');
        // Don't trigger session expired for missing timestamps
        return false;
      }

      final lastActive = DateTime.fromMillisecondsSinceEpoch(lastActiveTime);
      final now = DateTime.now();

      // Validate timestamps
      if (lastActive.isAfter(now)) {
        debugPrint('Session: Invalid timestamp detected (future date)');
        // Don't trigger session expired for invalid timestamps
        return false;
      }

      final difference = now.difference(lastActive);
      final differenceInMinutes = difference.inMinutes;

      // Check timeout - only timeout if app has been inactive for too long
      final isValid = differenceInMinutes < _sessionTimeoutMinutes;

      if (!isValid) {
        debugPrint(
          'Session: Timeout after $differenceInMinutes minutes of inactivity',
        );
        _setSessionExpired(true);
      } else {
        _setSessionExpired(false);
        debugPrint(
          'Session: Valid - last active $differenceInMinutes minutes ago',
        );
      }

      return isValid;
    } catch (e) {
      debugPrint('Session: Error checking validity - $e');
      // Don't trigger session expired for errors
      return false;
    }
  }

  // Set session expired state and notify listeners
  static void _setSessionExpired(bool value) {
    if (_isSessionExpired != value) {
      _isSessionExpired = value;
      _sessionExpiredController.add(value);
    }
  }

  // Get session status for UI components
  static bool get isExpired => _isSessionExpired;

  // Check if user is properly authenticated
  static bool get isAuthenticated {
    final currentUser = Supabase.instance.client.auth.currentUser;
    final currentSession = Supabase.instance.client.auth.currentSession;
    return currentUser != null && currentSession != null && !_isSessionExpired;
  }

  // Clear session data with proper cleanup
  static Future<void> clearSession() async {
    try {
      _stopSessionMonitoring();
      _setSessionExpired(true);

      // Clear local preferences
      final prefs = await SharedPreferences.getInstance();
      await prefs.remove(_lastActiveTimeKey);
      await prefs.remove(_sessionStateKey);

      // Sign out from Supabase
      await Supabase.instance.client.auth.signOut();

      debugPrint('Session: Cleared and signed out successfully');
    } catch (e) {
      debugPrint('Session: Error during cleanup - $e');
      // Even if there's an error, mark session as expired
      _setSessionExpired(true);
    }
  }

  // Refresh session - extend if still valid
  static Future<bool> refreshSession() async {
    try {
      final isValid = await isSessionValid();
      if (isValid) {
        await updateLastActiveTime();
        return true;
      }
      return false;
    } catch (e) {
      debugPrint('Session: Error refreshing - $e');
      return false;
    }
  }

  // Start session monitoring (check every minute when app is in background)
  static void _startSessionMonitoring() {
    if (!_isAppInBackground) {
      debugPrint('Session: Monitoring not needed - app in foreground');
      return;
    }

    _stopSessionMonitoring(); // Stop any existing timer

    _sessionCheckTimer = Timer.periodic(
      const Duration(minutes: 1), // Check every minute
      (timer) async {
        if (_isCheckingSession || !_isAppInBackground) return;

        _isCheckingSession = true;
        try {
          final isValid = await isSessionValid();
          if (!isValid) {
            debugPrint('Session: Expired during background monitoring');
            await clearSession();
            timer.cancel(); // Stop monitoring if session expired
          }
        } catch (e) {
          debugPrint('Session: Error in background monitoring - $e');
        } finally {
          _isCheckingSession = false;
        }
      },
    );
    debugPrint('Session: Background monitoring started');
  }

  // Stop session monitoring
  static void _stopSessionMonitoring() {
    if (_sessionCheckTimer != null) {
      _sessionCheckTimer!.cancel();
      _sessionCheckTimer = null;
      debugPrint('Session: Monitoring stopped');
    }
  }

  // Get current session timeout
  static int getSessionTimeout() {
    return _sessionTimeoutMinutes;
  }

  // Dispose resources
  static void dispose() {
    _stopSessionMonitoring();
    _sessionExpiredController.close();
  }
}
