import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/services/app_lifecycle_service.dart';
import 'package:http/http.dart' as http;

/// HTTP Client Manager with automatic retry logic for transient network errors
/// Handles background/foreground transitions gracefully
class HttpClientManager {
  final http.Client _client;
  final AppLifecycleService _lifecycleService;
  
  static const Duration _defaultTimeout = Duration(seconds: 30);
  static const int _maxRetries = 2;
  
  HttpClientManager({
    required AppLifecycleService lifecycleService,
    http.Client? client,
  })  : _lifecycleService = lifecycleService,
        _client = client ?? http.Client();
  
  /// Execute POST request with retry logic
  Future<http.Response> post({
    required Uri uri,
    Map<String, String>? headers,
    Object? body,
    Duration? timeout,
    int maxRetries = _maxRetries,
  }) async {
    return _executeWithRetry(
      () => _client
          .post(uri, headers: headers, body: body)
          .timeout(timeout ?? _defaultTimeout),
      endpoint: uri.pathSegments.isNotEmpty ? uri.pathSegments.last : uri.toString(),
      maxRetries: maxRetries,
    );
  }
  
  /// Execute GET request with retry logic
  Future<http.Response> get({
    required Uri uri,
    Map<String, String>? headers,
    Duration? timeout,
    int maxRetries = _maxRetries,
  }) async {
    return _executeWithRetry(
      () => _client
          .get(uri, headers: headers)
          .timeout(timeout ?? _defaultTimeout),
      endpoint: uri.pathSegments.isNotEmpty ? uri.pathSegments.last : uri.toString(),
      maxRetries: maxRetries,
    );
  }
  
  /// Core retry logic for HTTP requests
  Future<http.Response> _executeWithRetry(
    Future<http.Response> Function() request, {
    required String endpoint,
    int maxRetries = _maxRetries,
  }) async {
    int attempt = 0;
    
    while (attempt <= maxRetries) {
      try {
        // Wait for app to be in foreground before attempting request
        await _waitForAppForeground();
        
        // Check if app just came from background
        if (_lifecycleService.lastResumedTime != null && attempt == 0) {
          final timeSinceResume = DateTime.now().difference(_lifecycleService.lastResumedTime!);
          if (timeSinceResume.inSeconds < 5) {
            debugPrint('🔄 App recently resumed (${timeSinceResume.inSeconds}s ago), adding small delay before request...');
            await Future.delayed(const Duration(milliseconds: 500));
          }
        }
        
        return await request();
        
      } on SocketException catch (e) {
        if (attempt >= maxRetries) {
          // If app is in background, wait for it to come back before throwing
          if (_lifecycleService.isAppInBackground) {
            debugPrint('⏸️  App in background, waiting to resume before final error for $endpoint');
            await _waitForAppForeground();
          }
          rethrow;
        }
        await _handleRetry(
          attempt: attempt++,
          errorType: 'SocketException',
          endpoint: endpoint,
          errorDetails: e.message,
        );
      } on http.ClientException catch (e) {
        if (attempt >= maxRetries) {
          // If app is in background, wait for it to come back before throwing
          if (_lifecycleService.isAppInBackground) {
            debugPrint('⏸️  App in background, waiting to resume before final error for $endpoint');
            await _waitForAppForeground();
          }
          rethrow;
        }
        await _handleRetry(
          attempt: attempt++,
          errorType: 'ClientException',
          endpoint: endpoint,
          errorDetails: e.message,
        );
      } on TimeoutException catch (e) {
        // For timeout, only retry once
        if (attempt >= 1) {
          // If app is in background, wait for it to come back before throwing
          if (_lifecycleService.isAppInBackground) {
            debugPrint('⏸️  App in background, waiting to resume before final error for $endpoint');
            await _waitForAppForeground();
          }
          rethrow;
        }
        await _handleRetry(
          attempt: attempt++,
          errorType: 'TimeoutException',
          endpoint: endpoint,
          errorDetails: e.message ?? 'Request timed out',
        );
      }
    }
    
    throw Exception('Max retries exceeded for $endpoint');
  }
  
  /// Wait for app to be in foreground before proceeding
  Future<void> _waitForAppForeground() async {
    if (!_lifecycleService.isAppInBackground) {
      return; // App is already in foreground
    }
    
    debugPrint('⏸️  App is in background, pausing request until app resumes...');
    
    // Wait for app to resume with a timeout
    final completer = Completer<void>();
    late StreamSubscription<AppLifecycleState> subscription;
    
    // Set up a listener for app state changes
    subscription = _lifecycleService.appStateStream.listen((state) {
      if (state == AppLifecycleState.resumed) {
        if (!completer.isCompleted) {
          debugPrint('✅ App resumed, continuing with request');
          completer.complete();
        }
      }
    });
    
    // Also check current state in case it changed
    if (_lifecycleService.currentState == AppLifecycleState.resumed) {
      if (!completer.isCompleted) {
        completer.complete();
      }
    }
    
    try {
      // Wait for app to resume with 60 second timeout
      await completer.future.timeout(
        const Duration(seconds: 60),
        onTimeout: () {
          debugPrint('⚠️  Timeout waiting for app to resume');
        },
      );
    } finally {
      await subscription.cancel();
    }
    
    // Add small delay after resume to let things stabilize
    await Future.delayed(const Duration(milliseconds: 300));
  }
  
  /// Handle retry delay with exponential backoff and validation
  Future<void> _handleRetry({
    required int attempt,
    required String errorType,
    required String endpoint,
    required String errorDetails,
  }) async {
    // If app went to background during the error, wait for it to come back
    if (_lifecycleService.isAppInBackground) {
      debugPrint('⏸️  Error occurred while app in background, waiting for app to resume...');
      await _waitForAppForeground();
    }
    
    // Check network connectivity before retrying
    final hasConnection = await Utils.checkConnection(bypassConnectionCheck: true);
    if (!hasConnection) {
      debugPrint('⚠️  No network connection available, waiting before retry...');
      // Wait a bit and check again instead of immediately throwing
      await Future.delayed(const Duration(seconds: 2));
      final recheckConnection = await Utils.checkConnection(bypassConnectionCheck: true);
      if (!recheckConnection) {
        throw SocketException('No network connection available for retry');
      }
    }
    
    // Exponential backoff: 300ms, 900ms, 2700ms
    final delay = Duration(milliseconds: 300 * (1 << attempt));
    
    debugPrint(
      '🔄 Retry attempt ${attempt + 1}/$_maxRetries for $endpoint\n'
      '   Error: $errorType - $errorDetails\n'
      '   Waiting: ${delay.inMilliseconds}ms\n'
      '   App State: ${_lifecycleService.currentState}'
    );
    
    await Future.delayed(delay);
  }
  
  /// Close the HTTP client
  void dispose() {
    _client.close();
  }
}





