import 'dart:convert';
import 'dart:io';

import 'package:flutter_icmp_ping/flutter_icmp_ping.dart';
import 'package:uuvpn/constant/app_strings.dart';
import 'package:uuvpn/entity/server_entity.dart';
import 'package:uuvpn/models/base_model.dart';
import 'package:uuvpn/service/server_service.dart';
import 'package:uuvpn/utils/shared_preferences_util.dart';
import 'package:uuvpn/utils/common_util.dart';

enum PingType { ping, tcp }

class ServerModel extends BaseModel {
  List<ServerEntity> _serverEntityList = [];
  ServerEntity? _selectServerEntity;
  int _selectServerIndex = 0;

  final ServerService _serverService = ServerService();

  List<ServerEntity> get serverEntityList => _serverEntityList;

  ServerEntity? get selectServerEntity => _selectServerEntity;

  int get selectServerIndex => _selectServerIndex;

  getServerList({bool forceRefresh = false}) async {
    bool result = false;

    List<dynamic> data = await SharedPreferencesUtil.getInstance()
            ?.getList(AppStrings.serverNode) ??
        [];
    List<dynamic> newData =
        List.from(data.map((e) => Map<String, dynamic>.from(jsonDecode(e))));

    print("$newData");
    if (newData.isEmpty || forceRefresh) {
      setServerEntityList(await _serverService.server());
    } else {
      _serverEntityList = serverEntityFromList(newData);
    }

    notifyListeners();

    result = true;

    return result;
  }

  void pingAll() async {
    for (int i = 0; i < _serverEntityList.length; i++) {
      var duration = const Duration(milliseconds: 300);
      await Future.delayed(duration);
      ping(i);
    }
  }

  void ping(int index, {PingType type = PingType.tcp}) {
    ServerEntity serverEntity = _serverEntityList[index];
    String host = serverEntity.host;
    int serverPort = serverEntity.port;

    //print("host=$host");
    //print("serverPort=$serverPort");

    switch (type) {
      case PingType.ping:
        try {
          final ping =
              Ping(host, count: 1, timeout: 1.0, interval: 1.0, ipv6: false);
          ping.stream.listen((event) {
            print(event);
            if (event.error != null) {
              var duration = const Duration(minutes: 1);
              _serverEntityList[index].ping = duration;
            } else if (event.response != null) {
              _serverEntityList[index].ping = event.response?.time;
            }
            notifyListeners();

            ping.stop();
          });
        } catch (e) {
          rethrow;
        }
        break;
      case PingType.tcp:
        Stopwatch stopwatch = Stopwatch()..start();

        Socket.connect(host, serverPort, timeout: const Duration(seconds: 3))
            .then((socket) {
          socket.destroy();
          var duration = stopwatch.elapsed;
          _serverEntityList[index].ping = duration;

          notifyListeners();

          return duration;
        }).catchError((error) {
          var duration = const Duration(minutes: 1);
          _serverEntityList[index].ping = duration;

          throw error;
        });
        break;
      default:
        throw Error();
    }
  }

  getSelectServer() async {
    Map<String, dynamic> data = await SharedPreferencesUtil.getInstance()
            ?.getMap(AppStrings.selectServer) ??
        <String, dynamic>{};
    int index = int.parse(await SharedPreferencesUtil.getInstance()
            ?.getString(AppStrings.selectServerIndex) ??
        '0');

    if (data.isEmpty) {
      return null;
    }

    _selectServerEntity = ServerEntity.fromMap(data);
    _selectServerIndex = index;

    notifyListeners();

    return _selectServerEntity;
  }

  setServerEntityList(List<ServerEntity> serverEntityList) {
    _serverEntityList = serverEntityList;

    _saveServerEntityList();
  }

  setSelectServerEntity(ServerEntity selectServerEntity) {
    _selectServerEntity = selectServerEntity;

    _saveSelectServerEntity();

    notifyListeners();
  }

  setSelectServerIndex(int index) {
    _selectServerIndex = index;

    _saveSelectServerIndex();

    notifyListeners();
  }

  _saveServerEntityList() async {
    SharedPreferencesUtil? sharedPreferencesUtil =
        SharedPreferencesUtil.getInstance();

    await sharedPreferencesUtil?.setList(
        AppStrings.serverNode, _serverEntityList);
  }

  _saveSelectServerEntity() async {
    SharedPreferencesUtil? sharedPreferencesUtil =
        SharedPreferencesUtil.getInstance();

    await sharedPreferencesUtil?.setMap(
        AppStrings.selectServer, _selectServerEntity!.toMap());
  }

  _saveSelectServerIndex() async {
    SharedPreferencesUtil? sharedPreferencesUtil =
        SharedPreferencesUtil.getInstance();

    await sharedPreferencesUtil?.setString(
        AppStrings.selectServerIndex, _selectServerIndex.toString());
  }
}
