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

import 'package:device_info/device_info.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_udid/flutter_udid.dart';
import 'package:path_provider/path_provider.dart';
import 'package:road24_mobile/data/http/GeneralRequest.dart';
import 'package:road24_mobile/model/models/ChatMessage.dart';
import 'package:road24_mobile/model/models/LivechatConfigModel.dart';
import 'package:road24_mobile/model/models/rocket_chat_state.dart';
import 'package:road24_mobile/model/utils/uuid_utils.dart';
import 'package:road24_mobile/my_app.dart';
import 'package:road24_mobile/utils/frequent_widgets.dart';
import 'package:road24_mobile/view/base/splash/splash_screen.dart';
import 'package:road24_mobile/view/firebase.dart';

import 'package:web_socket_channel/io.dart';

class ChatRepository {
  final String _najotUrl = 'https://rocket.najotapp.uz';
  final String _wssUrl = 'wss://rocket.najotapp.uz/websocket';

  StreamSubscription? _rocketSubscription;
  String? deviceId;
  String? deviceModel;
  String? downloadsPath;
  static String? _roomId;

  static String _name = "";
  static String visitorToken = "";
  LivechatConfig? _livechatConfig;
  String subscibeRoom = 'FLUTTER_ROCKET';

  final _state = StreamController<RocketChatState>.broadcast();

  Stream<RocketChatState> get state => _state.stream;

  RocketChatState get initialState => RocketChatState(isLoading: true);
  RocketChatState lastState = RocketChatState();

  String? getTokenFromVariable() {
    return visitorToken;
  }

  void _saveState(RocketChatState state) {
    lastState = state;
    _state.sink.add(state);
  }

  void dispose() {
    _state.close();
    _rocketSubscription?.cancel();
    channel.sink.close();
  }

  Future<void> initRocketLivechat(String? name) async {
    // Rocket client configuring started

    _saveState(RocketChatState(isLoading: true));
    // _sharedPreferences = await SharedPreferences.getInstance();

    _livechatConfig = await getLivechatConfig();

    if (_livechatConfig == null) {
      return _saveState(RocketChatState(errorMessage: 'Connection error'));
    } else {
      _saveState(RocketChatState(config: _livechatConfig));
    }

    return startChatting(name);
  }

  Future<LivechatConfig?> getLivechatConfig() async {
    final url = '$_najotUrl/api/v1/livechat/config?token=$visitorToken';

    final result = await GeneralRequest.getRequest(url);

    if (result == null) return null;

    return LivechatConfig.fromJson(result);
  }

  Future<String> getVisitorToken() async {
    // get firebase token
    final token = MyApp.preferences?.getChatToken();
    // final token =
    //     "c7Xr_JVfkU9umu75ousfeL:APA91bH0mV1dH8dRGmIRm7hqaBkvpEF-Jv5ByD42DdNylDhKgHKXsQm15mPIbpxHLLPwlfhRoogbTz5o_Ah0YM53_aw-OAJDq5_o4YMF3UfA4PMld_Uw1gEFbtJ7riqMhjvGbvLhrWyw";
    if (token == null) {
      print('cannot get firebase token for device');
      await FirebaseNotifications()
          .firebaseCloudMessagingListeners(isChat: true);

      var chatToken =
          MyApp.preferences?.getChatToken() ?? UuidUtils.getRandomString(21);

      await MyApp.preferences?.setChatToken(chatToken);

      return chatToken;
    }
    return token;
  }

  Future<bool> registerVisitor(String username) async {
    visitorToken = await getVisitorToken();

    final url = '$_najotUrl/api/v1/livechat/visitor';

    final platform = Platform.isIOS
        ? 'ios'
        : Platform.isAndroid
            ? 'android'
            : 'unknown';

    deviceModel = MyApp.preferences?.getDeviceModel();

    deviceId = MyApp.preferences?.getDeviceId();

    if (deviceModel == null || deviceId == null) {
      getDeviceInfo();
    }
    final appVersion =
        "${SplashScreen.packageInfo!.version}(${SplashScreen.packageInfo!.buildNumber}";

    final ipAddress = await GeneralRequest.getMyIp();

    final result = await GeneralRequest.postRequest(
      url,
      body: <String, dynamic>{
        'visitor': <String, dynamic>{
          'name': username,
          'token': visitorToken,
          'customFields': [
            {'key': 'platform', 'value': platform, 'overwrite': true},
            {'key': 'deviceId', 'value': deviceId, 'overwrite': true},
            {'key': 'deviceModel', 'value': deviceModel, 'overwrite': true},
            {'key': 'appVersion', 'value': appVersion, 'overwrite': true},
            {'key': 'ipAddress', 'value': ipAddress, 'overwrite': true},
          ],
        }
      },
    );
    if (result == null) return false;
    return result['success'] ?? false;
  }

  void saveLastVisit() {
    MyApp.preferences?.setLastVisit(DateTime.now().toIso8601String());
  }

  startChatting(String? name) async {
    MyApp.preferences?.setChatStarted(true);

    if (name != null && name != "")
      _name = name;
    else
      _name = MyApp.preferences!.getPhoneNumber();

    _saveState(RocketChatState(
      isLoading: true,
      name: _name,
    ));

    var isUserRegistered = await registerVisitor(_name);

    if (!isUserRegistered) {
      _saveState(lastState.copyWith(
        isLoading: false,
        errorMessage: 'Error connecting to Rocket',
      ));
      print("couldn't register user");
      // throw Exception("Couldn't register user ");
    }
    var _liveChatConfig = await getLivechatConfig();

    bool isAgentOnline = _liveChatConfig?.config?.online ?? false;

    if (isAgentOnline) {
      await getUnreadMessagesCount();
      // await getUnreadMessagesCount();
      // try to create new room
      final isRoomCreated = await _createRoom();

      if (!isRoomCreated) {
        print('room was not created');
        return _saveState(lastState.copyWith(
          errorMessage: 'Error connecting to Rocket',
        ));
      }

      startRealTimeSocket();
    }

    _saveState(lastState.copyWith(
      config: _liveChatConfig,
      isLoading: false,
    ));
  }

  startRealTimeSocket() async {
    _startRealtimeSocket();

    final history = (await getChatHistory()).reversed.toList();

    _saveState(lastState.copyWith(history: history));
    return history;
  }

  Future<List<LivechatMessage>> getChatHistory() async {
    print("Chat history");

    final url =
        '$_najotUrl/api/v1/livechat/messages.history/$_roomId?token=$visitorToken';
    var result = await http.get(
      Uri.parse(url),
      // headers: headers,
    );

    dynamic resultBody = json.decode(utf8.decode(result.bodyBytes));

    final history = <LivechatMessage>[];

    if (resultBody == null) return history;

    if (resultBody['messages'] == null) return history;

    (resultBody['messages'] as List<dynamic>).forEach(
      (m) => history.add(LivechatMessage.fromJson(m)),
    );
    return history;
  }

  Future<DateTime?> _getLastVisit() async {
    final dateString = MyApp.preferences?.getLastVisit();

    if (dateString == null) return null;
    return DateTime.parse(dateString);
  }

  Future<void> getUnreadMessagesCount() async {
    final lastVIsit = await _getLastVisit();
    if (lastVIsit == null) return null;

    visitorToken = await getVisitorToken();
    _roomId = await _getRoomId();

    final history = await getChatHistory();
    if (history.isEmpty) return null;

    int unreadMsgs = 0;

    history.forEach((msg) async {
      if (msg.t == 'command') {
        if (msg.msg == 'promptTranscript') {
          await MyApp.preferences?.setRoomId(null);
        }
      }

      if (msg.timestamp != null && msg.timestamp!.isAfter(lastVIsit))
        unreadMsgs++;
    });

    return _saveState(lastState.copyWith(unreadMsgs: unreadMsgs));
  }

  late IOWebSocketChannel channel;
  void _startRealtimeSocket() {
    channel = IOWebSocketChannel.connect(Uri.parse(_wssUrl));

    // Socket client
    _rocketSubscription = channel.stream.listen((dynamic message) {
      final data = jsonDecode(message);

      // ensure connection
      if (data['msg'] == 'ping') {
        _sendRocketEvent(<String, dynamic>{'msg': 'pong'});
      }

      if (data['server_id'] == '0') {
        _connect();
      } else if (data['msg'] == 'connected') {
        _subscribeToRoomUpdates();
      } else if (data['msg'] == 'changed') {
        if (data['collection'] == 'stream-room-messages') {
          final args = data['fields']['args'];
          if (args != null) {
            if ((args as List<dynamic>).isNotEmpty) {
              // try {
              final message = LivechatMessage.fromJson(args.first);
              if (message.t == 'livechat-close') {
                return unsubscribeFromRoomUpdates();
              }

              final LivechatMessage? messageExists = lastState.history
                  ?.firstWhere((m) => m?.sId == message.sId,
                      orElse: () => null);

              if (messageExists == null) {
                lastState.history?.add(message);
                _saveState(lastState);
              }
              // } catch (ex) {}
            }
          }
        }
      }
    });
  }

  void unsubscribeFromRoomUpdates() {
    _sendRocketEvent(<String, dynamic>{
      'msg': 'unsub',
      'id': subscibeRoom,
    });

    MyApp.preferences?.setRoomId(null);

    MyApp.preferences?.setChatStarted(false);

    MyApp.preferences?.setLastVisit(null);

    _saveState(lastState.copyWith(finished: true, unreadMsgs: 0));
  }

  /// establish connection
  void _connect() {
    _sendRocketEvent(<String, dynamic>{
      'msg': 'connect',
      'version': '1',
      'support': ['1']
    });
  }

  /// subscribe to room messages
  void _subscribeToRoomUpdates() {
    _sendRocketEvent(<String, dynamic>{
      'msg': 'sub',
      'name': 'stream-room-messages',
      'id': subscibeRoom,
      'params': [
        _roomId,
        {
          'useCollection': false,
          'args': [
            {
              'token': visitorToken,
              'visitorToken': visitorToken,
            }
          ]
        }
      ],
    });

    _sendRocketEvent(<String, dynamic>{
      'msg': 'sub',
      'name': 'stream-livechat-room',
      'id': subscibeRoom,
      'params': [
        _roomId,
        {
          'useCollection': false,
          'args': [
            {
              'token': visitorToken,
            }
          ]
        }
      ]
    });
  }

  void _sendRocketEvent(dynamic message) =>
      channel.sink.add(jsonEncode(message));

  Future<bool> _createRoom() async {
    _roomId = await _getRoomId();
    if (!kReleaseMode) {
      print("_roomid...$_roomId");
      print("vis token...$visitorToken");
    }
    final url =
        '$_najotUrl/api/v1/livechat/room?token=$visitorToken&rid=$_roomId';

    final result = await GeneralRequest.getRequest(url);
    print(result);
    if (result == null) return false;

    if (result['newRoom'] ?? false) {
      // await MyApp.preferences?.setRoomIsSended(false);
      // if (MyApp.preferences?.getToken() != null ||
      //     MyApp.preferences?.getToken() != "") {
      //   await sendRoomIdToServer(result["room"]["_id"]);
      // }
      await MyApp.preferences?.setRoomId(result["room"]["_id"]);
    }

    return result['success'] ?? false;
  }

  Future<String> _getRoomId() async {
    String? room = MyApp.preferences?.getRoomId();

    if (room == null) {
      final uuId = await FlutterUdid.udid;

      room = '$uuId-' + UuidUtils.getRandomString(10);
    }

    return room;
  }

  // getRoomIdFromServer() async {
  //   var url = "${general.Config.baseApiUrl}rooms/me";

  //   try {
  //     var response = await doGetRequest(url);

  //     var roomId = response?.data["room_id"];

  //     if (roomId != null) {
  //       await MyApp.preferences?.setRoomId(roomId);
  //     }
  //   } catch (ex) {
  //     print("get server room id exception  $ex");
  //   }
  // }

  // sendRoomIdToServer(String roomId) async {
  //   var url = "${general.Config.baseApiUrl}rooms/";
  //   print("url...$url");
  //   var body = {"room_id": roomId};

  //   try {
  //     var response = await doPostRequest(url, data: body);

  //     if (response?.data != null) {
  //       await MyApp.preferences?.setRoomIsSended(true);
  //     }
  //   } catch (ex) {
  //     print("send room id exception  $ex");
  //   }
  // }

  Future<bool> closeChat() async {
    _saveState(lastState.copyWith(isLoading: true));

    final url = '$_najotUrl/api/v1/livechat/room.close';

    final result = await GeneralRequest.postRequest(
      url,
      body: <String, dynamic>{
        'rid': _roomId,
        'token': visitorToken,
      },
    );

    MyApp.preferences?.setChatStarted(false);

    _saveState(lastState.copyWith(isLoading: false));

    return result['success'] ?? false;
  }

  //
  // Send Message
  //

  /// send message to Rocket.Chat
  Future<void> sendTextMessage(
    String? message,
  ) async {
    if (message == null) return;

    if (message.isEmpty) return;

    return _sendLiveMessage(message);
  }

  Future<void> sendFileMessage(File? file, Function showUploadPercent) async {
    if (file == null) return;

    return _sendLiveFileMessage(file, showUploadPercent);
  }

  /// send message live
  Future<void> _sendLiveFileMessage(
      File file, Function showUploadPercent) async {
    final url = '$_najotUrl/api/v1/livechat/upload/$_roomId';

    final fileName = file.path.split('/').last;

    FormData formData = FormData.fromMap({
      "file": await MultipartFile.fromFile(file.path, filename: fileName),
    });

    Response<dynamic>? result = await Dio().post(url,
        options: Options(
          headers: {
            'x-visitor-token': visitorToken,
          },
        ), onSendProgress: (int sent, int total) {
      showUploadPercent(
          /*double.parse(*/
          (sent / total) /*.toStringAsFixed(2))*/,
          fileName,
          sent / total >= 0 && sent / total < 1);
    }, data: formData);

    if (result == null) {
      return _saveState(lastState.copyWith(
        lastMsgSent: false,
        isLoading: false,
      ));
    }
    final success = result.data['success'] ?? false;

    if (!success) {
      lastState.history?.add(LivechatMessage(
        alias: _name,
        msg: file.path.split('/').last,
      ));
    }

    _saveState(lastState.copyWith(
      lastMsgSent: success,
      isLoading: false,
    ));
  }

  // _getBusinessHours() async {
  //   print("name $_name  token  $visitorToken");

  //   final url = '$_najotUrl/api/v1/livechat/business-hour';

  //   var header = {"X-User-Id": _name, "X-Auth-Token": visitorToken};

  //   try {
  //     final result = await GeneralRequest.getRequest(url, headers: header);

  //     print("business hours.....$result");
  //   } catch (ex) {
  //     print("hours problem....$ex");
  //   }
  // }

  /// send message live
  Future<void> _sendLiveMessage(String message) async {
    final url = '$_najotUrl/api/v1/livechat/message';

    final result = await GeneralRequest.postRequest(
      url,
      body: <String, dynamic>{
        'token': visitorToken,
        'rid': _roomId,
        'msg': message,
      },
    );
    if (result == null) {
      return _saveState(lastState.copyWith(
        lastMsgSent: false,
        isLoading: false,
      ));
    }

    final success = result['success'] ?? false;
    if (!success) {
      lastState.history?.add(LivechatMessage(
        alias: _name,
        msg: message,
      ));
    }
    _saveState(lastState.copyWith(
      lastMsgSent: success,
      isLoading: false,
    ));
  }

  Future<bool> getDeviceInfo() async {
    var prefs = MyApp.preferences;

    var appDocDir;

    if (Platform.isIOS) {
      appDocDir = await getApplicationDocumentsDirectory();
    } else {
      appDocDir = await getExternalStorageDirectory();
    }

    if (prefs != null && appDocDir != null)
      await prefs.setDownloadPath(appDocDir.path);

    deviceId = prefs?.getDeviceId();

    deviceModel = prefs?.getDeviceModel();

    downloadsPath = prefs?.getDownloadPath();

    if (deviceModel == null) {
      DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();

      if (Platform.isAndroid) {
        AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;

        deviceModel = androidInfo.model;
      } else if (Platform.isIOS) {
        IosDeviceInfo iosInfo = await deviceInfo.iosInfo;

        deviceModel = iosInfo.name + " " + iosInfo.model;
      }
      prefs?.setDeviceModel(deviceModel);
    }

    if (deviceId == null) {
      deviceId = await FrequentWidgets.getDeviceId();

      return true;
    }

    return false;
  }
}
