/// Authentication State Provider
///
/// Manages authentication state for the Worker application.
/// Handles login, logout, and user session management.
///
/// Uses Riverpod 3.0 with code generation for type-safe state management.
library;

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/core/constants/api_constants.dart';
import 'package:worker/core/network/dio_client.dart';
import 'package:worker/core/services/frappe_auth_service.dart';
import 'package:worker/core/services/onesignal_service.dart';
import 'package:worker/features/auth/data/datasources/auth_local_datasource.dart';
import 'package:worker/features/auth/data/datasources/auth_remote_datasource.dart';
import 'package:worker/features/auth/domain/entities/user.dart';
import 'package:worker/core/services/analytics_service.dart';

part 'auth_provider.g.dart';

/// Provide FlutterSecureStorage instance
@riverpod
FlutterSecureStorage secureStorage(Ref ref) {
  return const FlutterSecureStorage(
    aOptions: AndroidOptions(encryptedSharedPreferences: true),
    iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
  );
}

/// Provide AuthLocalDataSource instance
@riverpod
AuthLocalDataSource authLocalDataSource(Ref ref) {
  final secureStorage = ref.watch(secureStorageProvider);
  return AuthLocalDataSource(secureStorage);
}

/// Provide FrappeAuthService instance
@riverpod
Future<FrappeAuthService> frappeAuthService(Ref ref) async {
  final dio = await ref.watch(dioProvider.future);
  final secureStorage = ref.watch(secureStorageProvider);
  return FrappeAuthService(dio, secureStorage);
}

/// Provide AuthRemoteDataSource instance
@riverpod
Future<AuthRemoteDataSource> authRemoteDataSource(Ref ref) async {
  final dio = await ref.watch(dioProvider.future);
  return AuthRemoteDataSource(dio);
}

/// Authentication state result
///
/// Represents the result of authentication operations.
/// Contains either the authenticated user or null if logged out.
typedef AuthState = AsyncValue<User?>;

/// Authentication Provider
///
/// Main provider for authentication state management.
/// Provides login and logout functionality with async state handling.
///
/// Usage in widgets:
/// ```dart
/// final authState = ref.watch(authProvider);
/// authState.when(
///   data: (user) => user != null ? HomeScreen() : LoginScreen(),
///   loading: () => LoadingIndicator(),
///   error: (error, stack) => ErrorWidget(error),
/// );
/// ```
@riverpod
class Auth extends _$Auth {
  /// Get auth local data source
  AuthLocalDataSource get _localDataSource =>
      ref.read(authLocalDataSourceProvider);

  /// Get Frappe auth service
  Future<FrappeAuthService> get _frappeAuthService async =>
      await ref.read(frappeAuthServiceProvider.future);

  /// Initialize with saved session if available
  @override
  Future<User?> build() async {
    // Simple initialization - just check if user is logged in
    // Do this ONCE on app startup and don't rebuild
    try {
      // Check if "Remember Me" was enabled
      final rememberMe = await _localDataSource.getRememberMe();

      if (!rememberMe) {
        // User didn't check "Remember Me", don't restore session
        return null;
      }

      // Check if we have a stored session
      final secureStorage = ref.read(secureStorageProvider);
      final sid = await secureStorage.read(key: 'frappe_sid');
      final userId = await secureStorage.read(key: 'frappe_user_id');
      final fullName = await secureStorage.read(key: 'frappe_full_name');

      if (sid != null && userId != null && userId != ApiConstants.frappePublicUserId) {
        // User is logged in and wants to be remembered
        // Restore OneSignal external user ID for targeted notifications
        await OneSignalService.login(userId);

        // Create User entity
        final now = DateTime.now();
        return User(
          userId: userId,
          phoneNumber: userId,
          fullName: fullName ?? 'User',
          email: '',
          role: UserRole.customer,
          status: UserStatus.active,
          loyaltyTier: LoyaltyTier.gold,
          totalPoints: 0,
          companyInfo: null,
          cccd: null,
          attachments: [],
          address: null,
          avatarUrl: null,
          referralCode: null,
          referredBy: null,
          erpnextCustomerId: null,
          createdAt: now.subtract(const Duration(days: 30)),
          updatedAt: now,
          lastLoginAt: now,
        );
      }
    } catch (e) {
      // Failed to check session, but don't prevent app from starting
      print('Failed to check saved session: $e');
    }

    return null;
  }

  /// Login with phone number
  ///
  /// Uses Frappe ERPNext API authentication flow:
  /// 1. Get current session from storage (should exist from app startup)
  /// 2. Call login API with phone number
  /// 3. Get new authenticated session with user credentials
  /// 4. Update FlutterSecureStorage with new session
  /// 5. Update Dio interceptors with new session for subsequent API calls
  /// 6. Save rememberMe preference if enabled
  ///
  /// Parameters:
  /// - [phoneNumber]: User's phone number (Vietnamese format)
  /// - [password]: User's password (reserved for future use, not sent yet)
  /// - [rememberMe]: If true, session will be restored on next app launch
  ///
  /// Returns: Authenticated User object on success
  ///
  /// Throws: Exception on authentication failure
  Future<void> login({
    required String phoneNumber,
    required String password,
    bool rememberMe = false,
  }) async {
    // Set loading state
    state = const AsyncValue.loading();

    state = await AsyncValue.guard(() async {
      // Validation
      if (phoneNumber.isEmpty) {
        throw Exception('Số điện thoại không được để trống');
      }

      final frappeService = await _frappeAuthService;

      // Get current session (should exist from app startup)
      final currentSession = await frappeService.getStoredSession();
      if (currentSession == null) {
        // If no session, get a new one
        await frappeService.getSession();
        final newSession = await frappeService.getStoredSession();
        if (newSession == null) {
          throw Exception('Failed to get session');
        }
      }

      // Call login API and store session
      final loginResponse = await frappeService.login(phoneNumber, password: password);

      // Save rememberMe preference
      await _localDataSource.saveRememberMe(rememberMe);

      // Set user ID for analytics tracking
      await AnalyticsService.setUserId(phoneNumber);
      // Log login event
      await AnalyticsService.logLogin(method: 'phone');

      // Set OneSignal external user ID for targeted notifications
      await OneSignalService.login(phoneNumber);

      // Create and return User entity
      final now = DateTime.now();
      return User(
        userId: phoneNumber,
        phoneNumber: phoneNumber,
        fullName: loginResponse.fullName,
        email: '',
        role: UserRole.customer,
        status: UserStatus.active,
        loyaltyTier: LoyaltyTier.gold,
        totalPoints: 0,
        companyInfo: null,
        cccd: null,
        attachments: [],
        address: null,
        avatarUrl: null,
        referralCode: null,
        referredBy: null,
        erpnextCustomerId: null,
        createdAt: now.subtract(const Duration(days: 30)),
        updatedAt: now,
        lastLoginAt: now,
      );
    });
  }

  /// Logout current user
  ///
  /// Clears authentication state and removes saved session.
  /// Gets a new public session for registration/login.
  Future<void> logout() async {
    state = const AsyncValue.loading();

    state = await AsyncValue.guard(() async {
      final frappeService = await _frappeAuthService;

      // Clear user ID from analytics
      await AnalyticsService.setUserId(null);

      // Clear OneSignal external user ID
      await OneSignalService.logout();

      // Clear saved session
      await _localDataSource.clearSession();
      await frappeService.clearSession();

      // Get new public session for registration/login
      await frappeService.getSession();

      // Return null to indicate logged out
      return null;
    });
  }

  /// Get current authenticated user
  ///
  /// Returns the current user if logged in, null otherwise.
  User? get currentUser => state.value;

  /// Check if user is authenticated
  ///
  /// Returns true if there is a logged-in user.
  bool get isAuthenticated => currentUser != null;

  /// Check if authentication is in progress
  ///
  /// Returns true during login/logout operations.
  bool get isLoading => state.isLoading;

  /// Get authentication error if any
  ///
  /// Returns error message or null if no error.
  Object? get error => state.error;
}

/// Convenience provider for checking if user is authenticated
///
/// Usage:
/// ```dart
/// final isLoggedIn = ref.watch(isAuthenticatedProvider);
/// if (isLoggedIn) {
///   // Show home screen
/// }
/// ```
@riverpod
bool isAuthenticated(Ref ref) {
  final authState = ref.watch(authProvider);
  return authState.value != null;
}

/// Convenience provider for getting current user
///
/// Usage:
/// ```dart
/// final user = ref.watch(currentUserProvider);
/// if (user != null) {
///   Text('Welcome ${user.fullName}');
/// }
/// ```
@riverpod
User? currentUser(Ref ref) {
  final authState = ref.watch(authProvider);
  return authState.value;
}

/// Convenience provider for user's loyalty tier
///
/// Returns the current user's loyalty tier or null if not logged in.
///
/// Usage:
/// ```dart
/// final tier = ref.watch(userLoyaltyTierProvider);
/// if (tier != null) {
///   Text('Tier: ${tier.displayName}');
/// }
/// ```
@riverpod
LoyaltyTier? userLoyaltyTier(Ref ref) {
  final user = ref.watch(currentUserProvider);
  return user?.loyaltyTier;
}

/// Convenience provider for user's total points
///
/// Returns the current user's total loyalty points or 0 if not logged in.
///
/// Usage:
/// ```dart
/// final points = ref.watch(userTotalPointsProvider);
/// Text('Points: $points');
/// ```
@riverpod
int userTotalPoints(Ref ref) {
  final user = ref.watch(currentUserProvider);
  return user?.totalPoints ?? 0;
}

/// Initialize Frappe session
///
/// Call this to ensure a Frappe session exists before making API calls.
/// This is separate from the Auth provider to avoid disposal issues.
///
/// Usage:
/// ```dart
/// // On login page or before API calls that need session
/// await ref.read(initializeFrappeSessionProvider.future);
/// ```
@riverpod
Future<void> initializeFrappeSession(Ref ref) async {
  try {
    final frappeService = await ref.watch(frappeAuthServiceProvider.future);

    // Check if we already have a session
    final storedSession = await frappeService.getStoredSession();

    if (storedSession == null) {
      // No session exists, get a public one
      await frappeService.getSession();
    }
  } catch (e) {
    // Log error but don't throw - allow app to continue
    print('Failed to initialize Frappe session: $e');
  }
}
