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

import 'package:flutter/foundation.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:uuid/uuid.dart';

import '../models/signaling.dart';
import 'auth_api.dart';
import 'im_service.dart';

/// 通话阶段
enum CallPhase {
  idle, // 空闲
  outgoing, // 呼出中（等待对方接听）
  incoming, // 来电中（等待本机接听）
  incall, // 通话中
}

/// 一对一语音通话：OpenIM 自定义消息做信令 + LiveKit 传声音。
/// 信令协议与官方样板工程一致（customType 200-204 的仅在线自定义消息）。
class CallService extends ChangeNotifier {
  CallService._();

  static final CallService instance = CallService._();

  final AuthApi _authApi = AuthApi();

  CallPhase phase = CallPhase.idle;

  /// 对方 userID 与显示名
  String? peerUserID;
  String peerName = '';

  /// 通话时长（秒）
  int callSeconds = 0;

  /// 麦克风是否开启
  bool micOn = true;

  /// 是否免提
  bool speakerOn = false;

  /// 有来电时的回调（由 main.dart 设置，用于弹出来电界面）
  void Function()? onIncomingCall;

  /// 通话结束时要提示给用户的一句话（可为空）
  String? endHint;

  InvitationInfo? _invitation;
  Room? _room;
  EventsListener<RoomEvent>? _roomListener;
  Timer? _timeoutTimer;
  Timer? _durationTimer;
  StreamSubscription<Message>? _signalingSub;
  bool _started = false;

  /// 来电铃声等待 / 呼叫超时时间（秒），与信令里的 timeout 一致
  static const int _inviteTimeoutSec = 30;

  /// 启动信令监听（登录成功后调用一次）
  void start() {
    if (_started) return;
    _started = true;
    _signalingSub = IMService.instance.onSignaling.listen(_onSignaling);
  }

  bool get isBusy => phase != CallPhase.idle;

  /// 发起呼叫（单聊）。返回错误提示，null 表示成功进入呼叫流程。
  Future<String?> startCall({required String peerUserID, required String peerName}) async {
    if (isBusy) return '正在通话中，请稍后再试';
    final im = IMService.instance;
    final myUserID = im.currentUserID;
    final authToken = im.currentToken;
    if (myUserID == null || authToken == null) return '登录状态已失效，请重新登录';

    _invitation = InvitationInfo(
      inviterUserID: myUserID,
      inviteeUserIDList: [peerUserID],
      roomID: const Uuid().v4(),
      timeout: _inviteTimeoutSec,
      initiateTime: DateTime.now().millisecondsSinceEpoch,
      mediaType: 'audio',
      sessionType: ConversationType.single,
      platformID: im.platformID,
    );
    this.peerUserID = peerUserID;
    this.peerName = peerName;

    try {
      // 1. 发呼叫信令（仅在线，不落地）
      await _sendSignaling(SignalingType.callingInvite, peerUserID);
      // 2. 取 LiveKit token 并进房（先进房等对方，对方接听信令到达后开始计时）
      await _joinRoom(myUserID, authToken);
      _setPhase(CallPhase.outgoing);
      _startTimeoutTimer(() {
        endHint = '对方暂时无人接听';
        _sendSignalingQuietly(SignalingType.callingCancel, peerUserID);
        _teardown();
      });
      return null;
    } on AuthException catch (e) {
      _teardown();
      return e.message;
    } catch (_) {
      _teardown();
      return '发起通话失败，请检查网络后重试';
    }
  }

  /// 接听来电
  Future<void> accept() async {
    if (phase != CallPhase.incoming) return;
    final im = IMService.instance;
    final myUserID = im.currentUserID;
    final authToken = im.currentToken;
    final inviter = _invitation?.inviterUserID;
    if (myUserID == null || authToken == null || inviter == null) {
      _teardown();
      return;
    }
    _timeoutTimer?.cancel();
    try {
      await _sendSignaling(SignalingType.callingAccept, inviter);
      await _joinRoom(myUserID, authToken);
      _beginIncall();
    } catch (_) {
      endHint = '接听失败，请检查网络';
      _sendSignalingQuietly(SignalingType.callingReject, inviter);
      _teardown();
    }
  }

  /// 拒接来电
  Future<void> reject() async {
    if (phase != CallPhase.incoming) return;
    final inviter = _invitation?.inviterUserID;
    if (inviter != null) await _sendSignalingQuietly(SignalingType.callingReject, inviter);
    _teardown();
  }

  /// 取消呼叫（呼出方主动取消）
  Future<void> cancel() async {
    if (phase != CallPhase.outgoing) return;
    final peer = peerUserID;
    if (peer != null) await _sendSignalingQuietly(SignalingType.callingCancel, peer);
    _teardown();
  }

  /// 挂断（通话中）
  Future<void> hangup() async {
    if (phase != CallPhase.incall) return;
    final peer = peerUserID;
    if (peer != null) await _sendSignalingQuietly(SignalingType.callingHungup, peer);
    _teardown();
  }

  /// 通话界面上的统一返回键：按当前阶段取消/挂断
  Future<void> leave() async {
    switch (phase) {
      case CallPhase.outgoing:
        await cancel();
        break;
      case CallPhase.incoming:
        await reject();
        break;
      case CallPhase.incall:
        await hangup();
        break;
      case CallPhase.idle:
        break;
    }
  }

  Future<void> toggleMic() async {
    micOn = !micOn;
    notifyListeners();
    try {
      await _room?.localParticipant?.setMicrophoneEnabled(micOn);
    } catch (_) {
      // 切换失败时回退状态
      micOn = !micOn;
      notifyListeners();
    }
  }

  Future<void> toggleSpeaker() async {
    speakerOn = !speakerOn;
    notifyListeners();
    try {
      await Hardware.instance.setSpeakerphoneOn(speakerOn);
    } catch (_) {
      speakerOn = !speakerOn;
      notifyListeners();
    }
  }

  // ---------------- 信令处理 ----------------

  void _onSignaling(Message msg) {
    final payload = IMService.parseSignaling(msg);
    if (payload == null) return;
    final myUserID = IMService.instance.currentUserID;

    switch (payload.type) {
      case SignalingType.callingInvite:
        // 只处理发给自己的单聊音频呼叫
        final invitation = payload.invitation;
        final invitees = invitation.inviteeUserIDList ?? [];
        if (!invitees.contains(myUserID)) return;
        if (invitation.mediaType != 'audio') return;
        if (isBusy) {
          // 占线：直接回拒接（UI 从简，无排队等待）
          _sendSignalingQuietly(SignalingType.callingReject, invitation.inviterUserID, invitation: invitation);
          return;
        }
        _invitation = invitation;
        peerUserID = invitation.inviterUserID;
        peerName = invitation.inviterUserID ?? '';
        _loadPeerName();
        _setPhase(CallPhase.incoming);
        _startTimeoutTimer(() {
          // 来电超时未接
          _teardown();
        });
        onIncomingCall?.call();
        break;
      case SignalingType.callingAccept:
        if (phase == CallPhase.outgoing && _sameRoom(payload)) {
          _timeoutTimer?.cancel();
          _beginIncall();
        }
        break;
      case SignalingType.callingReject:
        if ((phase == CallPhase.outgoing || phase == CallPhase.incall) && _sameRoom(payload)) {
          endHint = '对方拒绝了通话';
          _teardown();
        }
        break;
      case SignalingType.callingCancel:
        if (phase == CallPhase.incoming && _sameRoom(payload)) {
          endHint = '对方已取消';
          _teardown();
        }
        break;
      case SignalingType.callingHungup:
        if (phase == CallPhase.incall && _sameRoom(payload)) {
          endHint = '通话已结束';
          _teardown();
        }
        break;
    }
  }

  bool _sameRoom(SignalingPayload payload) {
    return payload.roomID != null && payload.roomID == _invitation?.roomID;
  }

  Future<void> _loadPeerName() async {
    final id = peerUserID;
    if (id == null) return;
    try {
      final list = await OpenIM.iMManager.userManager.getUsersInfo(userIDList: [id]);
      if (list.isNotEmpty) {
        final name = list.first.nickname ?? '';
        if (name.isNotEmpty && peerUserID == id) {
          peerName = name;
          notifyListeners();
        }
      }
    } catch (_) {
      // 拉不到名字就显示工号
    }
  }

  // ---------------- LiveKit ----------------

  Future<void> _joinRoom(String myUserID, String authToken) async {
    final roomID = _invitation?.roomID;
    if (roomID == null) throw AuthException('通话数据不完整');
    final credential = await _authApi.getRtcToken(room: roomID, identity: myUserID, authToken: authToken);

    _room = Room();
    _roomListener = _room!.createListener();
    _roomListener!
      ..on<RoomDisconnectedEvent>((event) {
        // 掉线或房间被销毁：直接结束
        if (phase == CallPhase.incall) endHint = '通话已结束';
        _teardown();
      })
      ..on<ParticipantDisconnectedEvent>((event) {
        // 对方离开房间
        if (phase == CallPhase.incall) {
          endHint = '通话已结束';
          _teardown();
        }
      });

    await _room!.connect(credential.liveURL, credential.token);
    await _room!.localParticipant?.setMicrophoneEnabled(micOn);
  }

  // ---------------- 内部工具 ----------------

  void _beginIncall() {
    callSeconds = 0;
    _durationTimer?.cancel();
    _durationTimer = Timer.periodic(const Duration(seconds: 1), (_) {
      callSeconds += 1;
      notifyListeners();
    });
    _setPhase(CallPhase.incall);
  }

  void _setPhase(CallPhase p) {
    phase = p;
    notifyListeners();
  }

  void _startTimeoutTimer(void Function() onTimeout) {
    _timeoutTimer?.cancel();
    _timeoutTimer = Timer(const Duration(seconds: _inviteTimeoutSec), onTimeout);
  }

  Future<void> _sendSignaling(int type, String recvUserID, {InvitationInfo? invitation}) async {
    final inv = invitation ?? _invitation;
    if (inv == null) return;
    final data = jsonEncode({'customType': type, 'data': inv.toJson()});
    final message = await OpenIM.iMManager.messageManager.createCustomMessage(
      data: data,
      extension: '',
      description: '',
    );
    await OpenIM.iMManager.messageManager.sendMessage(
      message: message,
      offlinePushInfo: OfflinePushInfo(),
      userID: recvUserID,
      isOnlineOnly: true,
    );
  }

  /// 发信令失败时静默处理（挂断/取消类消息失败不影响本地收尾）
  Future<void> _sendSignalingQuietly(int type, String? recvUserID, {InvitationInfo? invitation}) async {
    if (recvUserID == null) return;
    try {
      await _sendSignaling(type, recvUserID, invitation: invitation);
    } catch (_) {}
  }

  /// 结束通话：释放房间与计时器，回到空闲态
  void _teardown() {
    _timeoutTimer?.cancel();
    _timeoutTimer = null;
    _durationTimer?.cancel();
    _durationTimer = null;
    final room = _room;
    _room = null;
    _roomListener?.dispose();
    _roomListener = null;
    if (room != null) {
      () async {
        try {
          await room.disconnect();
          await room.dispose();
        } catch (_) {}
      }();
    }
    // 复位免提
    if (speakerOn) {
      Hardware.instance.setSpeakerphoneOn(false).catchError((_) {});
      speakerOn = false;
    }
    micOn = true;
    callSeconds = 0;
    _invitation = null;
    _setPhase(CallPhase.idle);
  }

  /// 消费结束提示（界面弹出提示后清空）
  String? takeEndHint() {
    final hint = endHint;
    endHint = null;
    return hint;
  }

  @override
  void dispose() {
    _signalingSub?.cancel();
    _teardown();
    super.dispose();
  }
}
