import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:guru_utils/extensions/extensions.dart';
import 'package:guru_utils/log/log.dart';

/// Created by Haoyi on 2022/8/16

class ConnectivityTrack {
  final ConnectivityResult oldResult;
  final ConnectivityResult newResult;

  static const ConnectivityTrack invalid =
      ConnectivityTrack(ConnectivityResult.none, ConnectivityResult.none);

  const ConnectivityTrack(this.newResult, this.oldResult);

  ConnectivityTrack update(ConnectivityResult newResult) =>
      ConnectivityTrack(newResult, this.newResult);

  @override
  String toString() {
    return 'ConnectivityTrack{$oldResult => $newResult}';
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is ConnectivityTrack &&
          runtimeType == other.runtimeType &&
          oldResult == other.oldResult &&
          newResult == other.newResult;

  @override
  int get hashCode => oldResult.hashCode ^ newResult.hashCode;
}

class NetworkUtils {
  static final _connectivity = Connectivity();

  static ConnectivityResult? _mockConnectivityStatus;

  static final BehaviorSubject<ConnectivityTrack> _connectivityTrackSubject =
      BehaviorSubject.seeded(ConnectivityTrack.invalid);

  static Stream<ConnectivityTrack> get observableConnectivityTrack =>
      _connectivityTrackSubject.stream;

  static void setMockConnectivityStatus(ConnectivityResult? status) {
    _mockConnectivityStatus = status;
  }

  static void _updateConnectivityStatus(ConnectivityResult status) {
    final track = _connectivityTrackSubject.value.update(status);
    _connectivityTrackSubject.addIfChanged(track);
    Log.i("_updateConnectivityStatus: Network track $track");
  }

  static Future init() async {
    final status = await _connectivity.checkConnectivity();
    _updateConnectivityStatus(status);
    _connectivity.onConnectivityChanged.listen((newResult) {
      Log.d("Network status changed! $newResult");
      _updateConnectivityStatus(newResult);
    });
  }

  static Future<bool> isNetworkConnected() async {
    try {
      final connectivity = _mockConnectivityStatus ?? await _connectivity.checkConnectivity();
      if (connectivity == ConnectivityResult.none) {
        Log.w(
            "The current network status is invalid! Please try again after connecting to the network");
        return false;
      }
      return true;
    } catch (error, stacktrace) {
      return false;
    }
  }
}
