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

import 'package:flutter/foundation.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:path_provider/path_provider.dart';

import '../config.dart';
import '../models/signaling.dart';

/// SDK 与服务器的连接状态
enum ConnectStatus { idle, connecting, success, failed }

/// 通话信令的自定义消息类型（与官方样板工程保持一致，保证互通）
class SignalingType {
  SignalingType._();

  static const int callingInvite = 200; // 发起呼叫
  static const int callingAccept = 201; // 接听
  static const int callingReject = 202; // 拒接
  static const int callingCancel = 203; // 取消呼叫
  static const int callingHungup = 204; // 挂断
}

/// OpenIM SDK 的统一封装：初始化、登录、监听、会话列表。
/// 用 ChangeNotifier 做状态分发，不引入额外状态管理框架。
class IMService extends ChangeNotifier {
  IMService._();

  static final IMService instance = IMService._();

  /// SDK 是否已初始化（App 启动时做一次）
  bool sdkReady = false;

  /// 与服务器的连接状态（断网横幅用）
  ConnectStatus connectStatus = ConnectStatus.idle;

  /// 最近一次连接失败的原始错误（诊断展示用）
  String? lastConnectError;

  /// SDK 数据/日志目录（_doInit 时记录）
  String? _dataDir;

  /// 读 SDK 日志文件的结尾部分（定位「SDK 卡在哪」用）。读不到返回空串。
  Future<String> sdkLogTail() async {
    try {
      final dataDir = _dataDir ?? '${(await getApplicationDocumentsDirectory()).path}/openim';
      final dir = Directory(dataDir);
      if (!dir.existsSync()) return '';
      final files = dir
          .listSync(recursive: true)
          .whereType<File>()
          .where((f) => f.path.endsWith('.log'))
          .toList()
        ..sort((a, b) => b.lastModifiedSync().compareTo(a.lastModifiedSync()));
      if (files.isEmpty) return '';
      final file = files.first;
      final len = file.lengthSync();
      final start = len > 2048 ? len - 2048 : 0;
      final raf = file.openSync();
      try {
        raf.setPositionSync(start);
        final bytes = raf.readSync(len - start);
        // 去掉日志里的 ANSI 颜色码，免得截图里出现乱码
        return String.fromCharCodes(bytes)
            .replaceAll(RegExp(r'\x1B\[[0-9;]*m'), '')
            .trim();
      } finally {
        raf.closeSync();
      }
    } catch (_) {
      return '';
    }
  }

  /// 是否已登录
  bool loggedIn = false;

  /// 会话首次同步中（消息列表的「正在加载」态）
  bool syncing = true;

  /// 会话同步失败（消息列表的「加载失败」态）
  bool syncFailed = false;

  /// 会话同步是否完成过至少一次（区分「正在加载」和「还没有消息」）
  bool conversationsLoaded = false;

  /// 会话列表（置顶在前，其余按最新消息时间倒序）
  List<ConversationInfo> conversations = [];

  /// 当前登录用户信息
  UserInfo? selfInfo;

  /// 当前登录凭证（发起通话取 LiveKit token 时要用）
  String? currentUserID;
  String? currentToken;

  /// 被踢下线 / token 失效时的回调（由 main.dart 设置：清缓存、回登录页）
  void Function()? onForceLogout;

  /// 收到新消息（聊天页订阅）
  final StreamController<Message> _newMsgController = StreamController<Message>.broadcast();
  Stream<Message> get onNewMessage => _newMsgController.stream;

  /// 消息被撤回（撤回方的 clientMsgID，聊天页订阅）
  final StreamController<String> _revokeController = StreamController<String>.broadcast();
  Stream<String> get onMessageRevoked => _revokeController.stream;

  /// 收到通话信令（仅在线自定义消息，通话模块订阅）
  final StreamController<Message> _signalingController = StreamController<Message>.broadcast();
  Stream<Message> get onSignaling => _signalingController.stream;

  int get platformID => Platform.isIOS ? IMPlatform.ios : IMPlatform.android;

  /// App 启动时初始化 SDK（只做一次）。
  /// 用共享 Future 防止超时后重复触发 initSDK：第一次调用还在跑时，
  /// 后续调用直接复用同一个 Future；失败了则允许下次重试。
  Future<void>? _initFuture;

  Future<void> init() {
    if (sdkReady) return Future.value();
    final inFlight = _initFuture;
    if (inFlight != null) return inFlight;
    final future = _doInit();
    _initFuture = future;
    return future.whenComplete(() {
      // 失败的初始化允许重试；成功的保留 sdkReady 标记即可
      if (!sdkReady && identical(_initFuture, future)) _initFuture = null;
    });
  }

  /// 初始化进行到哪一步（超时时展示，区分卡在获取目录还是 SDK 内核）
  String lastInitStep = '还没开始';

  Future<void> _doInit() async {
    lastInitStep = '① 获取存储目录';
    final dir = await getApplicationDocumentsDirectory();
    final dataDir = '${dir.path}/openim';
    _dataDir = dataDir;
    lastInitStep = '② 创建数据目录';
    await Directory(dataDir).create(recursive: true);
    lastInitStep = '③ 启动消息组件内核';
    final ok = await OpenIM.iMManager.initSDK(
      platformID: platformID,
      apiAddr: apiAddr,
      wsAddr: wsAddr,
      dataDir: dataDir,
      logFilePath: dataDir,
      logLevel: 6,
      listener: OnConnectListener(
        onConnecting: () {
          connectStatus = ConnectStatus.connecting;
          notifyListeners();
        },
        onConnectFailed: (code, error) {
          connectStatus = ConnectStatus.failed;
          lastConnectError = '$code $error';
          notifyListeners();
        },
        onConnectSuccess: () {
          connectStatus = ConnectStatus.success;
          lastConnectError = null;
          notifyListeners();
        },
        onKickedOffline: _forceLogout,
        onUserTokenExpired: _forceLogout,
        onUserTokenInvalid: _forceLogout,
      ),
    );
    lastInitStep = '④ 内核已返回（结果：$ok）';
    sdkReady = ok == true;
    _setBusinessListeners();
    notifyListeners();
  }

  void _setBusinessListeners() {
    OpenIM.iMManager.conversationManager.setConversationListener(
      OnConversationListener(
        onSyncServerStart: (reInstall) {
          syncing = true;
          syncFailed = false;
          notifyListeners();
        },
        onSyncServerFinish: (reInstall) {
          syncing = false;
          conversationsLoaded = true;
          refreshConversations();
        },
        onSyncServerFailed: (reInstall) {
          syncing = false;
          syncFailed = true;
          notifyListeners();
        },
        onConversationChanged: (list) => refreshConversations(),
        onNewConversation: (list) => refreshConversations(),
        onTotalUnreadMessageCountChanged: (count) => notifyListeners(),
      ),
    );
    OpenIM.iMManager.messageManager.setAdvancedMsgListener(
      OnAdvancedMsgListener(
        onRecvNewMessage: (msg) {
          _newMsgController.add(msg);
        },
        onRecvOfflineNewMessage: (msg) {
          _newMsgController.add(msg);
        },
        onNewRecvMessageRevoked: (info) {
          if (info.clientMsgID != null) _revokeController.add(info.clientMsgID!);
        },
        onRecvOnlineOnlyMessage: (msg) {
          // 通话信令走「仅在线」的自定义消息，转发给通话模块
          if (msg.contentType == MessageType.custom) {
            _signalingController.add(msg);
          }
        },
      ),
    );
    // 好友与群变更只负责刷新界面（通讯录页直接监听 IMService）
    OpenIM.iMManager.friendshipManager.setFriendshipListener(
      OnFriendshipListener(
        onFriendAdded: (info) => notifyListeners(),
        onFriendDeleted: (info) => notifyListeners(),
        onFriendInfoChanged: (info) => notifyListeners(),
        onFriendApplicationAdded: (info) => notifyListeners(),
        onFriendApplicationAccepted: (info) => notifyListeners(),
        onFriendApplicationRejected: (info) => notifyListeners(),
      ),
    );
    OpenIM.iMManager.groupManager.setGroupListener(
      OnGroupListener(
        onJoinedGroupAdded: (info) => notifyListeners(),
        onJoinedGroupDeleted: (info) => notifyListeners(),
        onGroupInfoChanged: (info) => notifyListeners(),
      ),
    );
  }

  /// 登录（init 之后调用）。userID/token 来自公司账号登录接口。
  Future<void> login({required String userID, required String token}) async {
    await init();
    if (!sdkReady) {
      // initSDK 返回 false：SDK 没初始化成功，直接登录会无限等待
      throw StateError('消息组件初始化失败');
    }
    final user = await OpenIM.iMManager.login(
      userID: userID,
      token: token,
      defaultValue: () async => UserInfo(userID: userID),
    );
    currentUserID = userID;
    currentToken = token;
    selfInfo = user;
    loggedIn = true;
    notifyListeners();
    // SDK 登录期间会话同步回调可能先于 loggedIn=true 触发，refreshConversations
    // 会因登录态守卫而提前返回，导致会话列表停在空态。登录完成后等会话同步
    // （onSyncServerFinish 置 conversationsLoaded）真正结束再刷新一次。
    await _waitSyncThenRefresh();
  }

  /// 等待 SDK 会话同步完成（最多约 10s），随后拉取会话列表。
  Future<void> _waitSyncThenRefresh() async {
    for (var i = 0; i < 12; i++) {
      if (conversationsLoaded) {
        await refreshConversations();
        return;
      }
      await Future.delayed(const Duration(milliseconds: 800));
    }
    // 超时兜底：仍补一次（refreshConversations 内部有重试）
    await refreshConversations();
  }

  /// 刷新自己的资料（我的页面展示用）
  Future<void> refreshSelfInfo() async {
    if (!loggedIn) return;
    try {
      selfInfo = await OpenIM.iMManager.userManager.getSelfUserInfo();
      notifyListeners();
    } catch (_) {
      // 拉取失败沿用内存里的旧数据
    }
  }

  /// 退出登录
  Future<void> logout() async {
    try {
      await OpenIM.iMManager.logout();
    } catch (_) {
      // 本地照常清理
    }
    _resetLoginState();
  }

  void _forceLogout() {
    _resetLoginState();
    onForceLogout?.call();
  }

  void _resetLoginState() {
    loggedIn = false;
    currentUserID = null;
    currentToken = null;
    selfInfo = null;
    conversations = [];
    conversationsLoaded = false;
    syncing = true;
    syncFailed = false;
    notifyListeners();
  }

  /// 重新拉取全部会话并排序（置顶在前，其余按最新消息时间倒序）。
  /// SDK 登录刚完成时立刻调用可能拿到 10009「登录未完成」，这里做短重试。
  Future<void> refreshConversations() async {
    if (!loggedIn) return;
    for (var attempt = 0; attempt < 5; attempt++) {
      try {
        final list = await OpenIM.iMManager.conversationManager.getAllConversationList();
        list.sort((a, b) {
          final ap = a.isPinned == true ? 0 : 1;
          final bp = b.isPinned == true ? 0 : 1;
          if (ap != bp) return ap - bp;
          return (b.latestMsgSendTime ?? 0).compareTo(a.latestMsgSendTime ?? 0);
        });
        conversations = list;
        conversationsLoaded = true;
        notifyListeners();
        return;
      } catch (_) {
        // 登录尚未完成或瞬时失败，短暂等待后重试
        await Future.delayed(const Duration(milliseconds: 800));
      }
    }
  }

  /// 进入聊天页后清除该会话未读
  Future<void> markConversationRead(String conversationID) async {
    try {
      await OpenIM.iMManager.conversationManager.markConversationMessageAsRead(conversationID: conversationID);
    } catch (_) {
      // 标记失败不影响聊天
    }
  }

  /// 解析「仅在线」自定义消息里的通话信令，不是通话信令返回 null
  static SignalingPayload? parseSignaling(Message msg) {
    try {
      final data = msg.customElem?.data;
      if (data == null || data.isEmpty) return null;
      final map = jsonDecode(data) as Map<String, dynamic>;
      final customType = map['customType'];
      if (customType is! int || customType < 200 || customType > 204) return null;
      final payload = SignalingPayload(
        type: customType,
        invitation: InvitationInfo.fromJson(Map<String, dynamic>.from(map['data'] ?? {})),
      );
      return payload;
    } catch (_) {
      return null;
    }
  }
}

/// 一条通话信令
class SignalingPayload {
  /// 见 [SignalingType]
  final int type;
  final InvitationInfo invitation;

  SignalingPayload({required this.type, required this.invitation});

  /// 房间号
  String? get roomID => invitation.roomID;

  /// 呼叫发起人
  String? get inviterUserID => invitation.inviterUserID;
}
