import 'dart:async';

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:openim_common/openim_common.dart';
import 'package:openim_live/src/widgets/live_button.dart';
import 'package:openim_live/src/widgets/loading_view.dart';
import 'package:synchronized/synchronized.dart';

import '../../../live_client.dart';

class ControlsView extends StatefulWidget {
  const ControlsView({
    Key? key,
    this.initState = CallState.call,
    this.callType = CallType.video,
    required this.callStateStream,
    required this.roomDidUpdateStream,
    this.userInfo,
    this.onMinimize,
    this.onCallingDuration,
    this.onEnabledMicrophone,
    this.onEnabledSpeaker,
    this.onCancel,
    this.onHangUp,
    this.onPickUp,
    this.onReject,
    this.onChangedCallState,
  }) : super(key: key);
  final Stream<Room> roomDidUpdateStream;
  final Stream<CallState> callStateStream;
  final CallState initState;
  final CallType callType;
  final UserInfo? userInfo;
  final Function()? onMinimize;
  final Function(int duration)? onCallingDuration;
  final Function(bool enabled)? onEnabledMicrophone;
  final Function(bool enabled)? onEnabledSpeaker;
  final Function()? onPickUp;
  final Function()? onCancel;
  final Function()? onReject;
  final Function(bool isPositive)? onHangUp;
  final Function(CallState state)? onChangedCallState;

  @override
  State<ControlsView> createState() => _ControlsViewState();
}

class _ControlsViewState extends State<ControlsView> {
  late CallState _callState;
  Timer? _callingTimer;
  int _callingDuration = 0;
  String _callingDurationStr = "00:00";

  //
  CameraPosition position = CameraPosition.front;

  List<MediaDevice>? _audioInputs;
  List<MediaDevice>? _audioOutputs;
  List<MediaDevice>? _videoInputs;

  StreamSubscription<CallState>? _callStateChangedSub;
  StreamSubscription? _deviceChangeSub;
  StreamSubscription<Room>? _roomDidUpdateSub;

  Room? _room;
  LocalParticipant? _participant;

  /// 默认启用麦克风
  bool _enabledMicrophone = true;

  /// 语音默认听筒，视频默认扬声器
  late bool _enabledSpeaker;

  final _lockAudio = Lock();
  final _lockSpeaker = Lock();

  @override
  void dispose() {
    _callStateChangedSub?.cancel();
    _roomDidUpdateSub?.cancel();
    _callingTimer?.cancel();
    _deviceChangeSub?.cancel();
    _participant?.removeListener(_onChange);
    super.dispose();
  }

  @override
  void initState() {
    _enabledSpeaker = widget.callType != CallType.audio;
    _onChangedCallState(widget.initState);
    _callStateChangedSub = widget.callStateStream.listen(_onChangedCallState);
    _roomDidUpdateSub = widget.roomDidUpdateStream.listen(_roomDidUpdate);
    // _queryUserInfo();

    _deviceChangeSub = Hardware.instance.onDeviceChange.stream.listen(_loadDevices);
    Hardware.instance.enumerateDevices().then(_loadDevices);
    super.initState();
  }

  _roomDidUpdate(Room room) {
    _room ??= room;
    if (room.localParticipant != null && _participant == null) {
      _participant = room.localParticipant;
      _participant?.addListener(_onChange);
    }
    Hardware.instance.setSpeakerphoneOn(_enabledSpeaker);
  }

  _onChangedCallState(CallState state) {
    if (!mounted) return;
    widget.onChangedCallState?.call(state);
    setState(() {
      _callState = state;
      if (_callState == CallState.calling) {
        _startCallingTimer();
      }
    });
  }

  void _startCallingTimer() {
    _callingTimer ??= Timer.periodic(const Duration(seconds: 1), (timer) {
      if (!mounted) return;
      setState(() {
        _callingDurationStr = IMUtils.seconds2HMS(++_callingDuration);
        widget.onCallingDuration?.call(_callingDuration);
      });
    });
  }

  void _loadDevices(List<MediaDevice> devices) async {
    _audioInputs = devices.where((d) => d.kind == 'audioinput').toList();
    _audioOutputs = devices.where((d) => d.kind == 'audiooutput').toList();
    _videoInputs = devices.where((d) => d.kind == 'videoinput').toList();
    // setState(() {});
  }

  void _onChange() {
    // trigger refresh
    setState(() {});
  }

  void _toggleAudio() async {
    await _lockAudio.synchronized(() async {
      _enabledMicrophone = !_enabledMicrophone;
      widget.onEnabledMicrophone?.call(_enabledMicrophone);
      if (_enabledMicrophone) {
        await _enableAudio();
      } else {
        await _disableAudio();
      }
      if (mounted) setState(() {});
    });

    // if (null != _participant) {
    //   if (_participant!.isMicrophoneEnabled()) {
    //     _disableAudio();
    //   } else {
    //     _enableAudio();
    //   }
    // }
  }

  void _toggleSpeaker() async {
    await _lockSpeaker.synchronized(() async {
      _enabledSpeaker = !_enabledSpeaker;
      widget.onEnabledSpeaker?.call(_enabledSpeaker);
      if (_enabledSpeaker) {
        await _enableSpeaker();
      } else {
        await _disableSpeaker();
      }
      setState(() {});
    });
  }

  Future<void> _disableAudio() async {
    await _participant?.setMicrophoneEnabled(false);
  }

  Future<void> _enableAudio() async {
    await _participant?.setMicrophoneEnabled(true);
  }

  Future<void> _disableVideo() async {
    await _participant?.setCameraEnabled(false);
  }

  Future<void> _enableVideo() async {
    await _participant?.setCameraEnabled(true, cameraCaptureOptions: CameraCaptureOptions(cameraPosition: position));
  }

  Future<void> _disableSpeaker() async {
    await Hardware.instance.setSpeakerphoneOn(false);
  }

  Future<void> _enableSpeaker() async {
    await Hardware.instance.setSpeakerphoneOn(true);
  }

  void _selectAudioOutput(MediaDevice device) async {
    await _room?.setAudioOutputDevice(device);
    setState(() {});
  }

  void _selectAudioInput(MediaDevice device) async {
    await _room?.setAudioInputDevice(device);
    setState(() {});
  }

  void _selectVideoInput(MediaDevice device) async {
    await _room?.setVideoInputDevice(device);
    setState(() {});
  }

  void _toggleCamera() async {
    //
    final track = _participant?.videoTrackPublications.firstOrNull?.track;
    if (track == null) return;
    Helper.switchCamera(track.mediaStreamTrack);
    // try {
    //   final newPosition = position.switched();
    //   await track.setCameraPosition(newPosition);
    //   // setState(() {
    //   //   position = newPosition;
    //   // });
    // } catch (error, stack) {
    //   Logger.print('could not restart track: $error $stack');
    //   return;
    // }
  }

  bool get _isEnding =>
      _callState == CallState.hangup ||
      _callState == CallState.beHangup ||
      _callState == CallState.cancel ||
      _callState == CallState.beCanceled ||
      _callState == CallState.timeout ||
      _callState == CallState.beRejected ||
      _callState == CallState.reject ||
      _callState == CallState.networkError;

  @override
  Widget build(BuildContext context) {
    final topInset = MediaQuery.of(context).padding.top;
    return Stack(
      children: [
        Positioned(
          left: 16,
          top: topInset + 8,
          child: SizedBox(
            width: Styles.callMinimizeHit,
            height: Styles.callMinimizeHit,
            child: IconButton(
              padding: EdgeInsets.zero,
              icon: Icon(Icons.keyboard_arrow_down, color: Styles.c_FFFFFF, size: Styles.callIconSize),
              onPressed: widget.onMinimize,
            ),
          ),
        ),
        if (null != _participant && isVideo)
          Positioned(
            right: 16,
            top: topInset + 8,
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                IconButton(
                  icon: Icon(
                    _participant!.isCameraEnabled() ? Icons.videocam_off_outlined : Icons.videocam_outlined,
                    color: Styles.c_FFFFFF,
                    size: Styles.callIconSize,
                  ),
                  onPressed: _participant!.isCameraEnabled() ? _disableVideo : _enableVideo,
                ),
                IconButton(
                  icon: Icon(Icons.cameraswitch_outlined, color: Styles.c_FFFFFF, size: Styles.callIconSize),
                  onPressed: _toggleCamera,
                ),
              ],
            ),
          ),
        Positioned.fill(child: _infoColumn),
        Positioned(
          left: 0,
          right: 0,
          bottom: 32 + MediaQuery.of(context).padding.bottom,
          child: _buttonArea,
        ),
        if (_callState == CallState.connecting) const LiveLoadingView(),
      ],
    );
  }

  Widget get _infoColumn {
    final nickname = widget.userInfo == null
        ? ''
        : (IMUtils.emptyStrToNull(widget.userInfo!.remark) ?? widget.userInfo!.nickname ?? '');
    final faceURL = widget.userInfo?.faceURL;
    return Column(
      children: [
        SizedBox(height: Styles.callNameTop),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Text(
            nickname,
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: Styles.ts_callName,
          ),
        ),
        const SizedBox(height: 8),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Text(
            _statusText,
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: isCalling && !isVideo ? Styles.ts_callTimer : Styles.ts_callStatus,
          ),
        ),
        const SizedBox(height: 32),
        AvatarView(
          width: Styles.callAvatar,
          height: Styles.callAvatar,
          text: nickname,
          url: faceURL,
          borderRadius: BorderRadius.circular(Styles.callAvatar * 0.18),
        ),
      ],
    );
  }

  String get _statusText {
    if (_callState == CallState.call) {
      return isVideo ? StrRes.waitingVideoCallHint : StrRes.waitingVoiceCallHint;
    }
    if (_callState == CallState.beCalled) {
      return isVideo ? StrRes.invitedVideoCallHint : StrRes.invitedVoiceCallHint;
    }
    if (_callState == CallState.connecting) {
      return StrRes.connecting;
    }
    if (_callState == CallState.networkError) {
      return StrRes.callNetworkInterrupted;
    }
    if (_callState == CallState.beRejected || _callState == CallState.reject) {
      return StrRes.rejectedByCaller;
    }
    if (_isEnding) {
      return StrRes.callEnded;
    }
    if (isCalling) {
      return _callingDurationStr;
    }
    return '';
  }

  Widget get _buttonArea {
    if (_callState == CallState.call ||
        (_callState == CallState.connecting && widget.initState == CallState.call)) {
      return Center(child: LiveButton.cancel(onTap: widget.onCancel));
    }
    if (_callState == CallState.beCalled) {
      return Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          LiveButton.reject(onTap: widget.onReject),
          SizedBox(width: Styles.callIncomingGap),
          LiveButton.pickUp(onTap: widget.onPickUp),
        ],
      );
    }
    if (_callState == CallState.calling) {
      return Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              LiveButton.mute(muted: !_enabledMicrophone, onTap: _toggleAudio),
              const SizedBox(width: 48),
              LiveButton.handsFree(on: _enabledSpeaker, onTap: _toggleSpeaker),
            ],
          ),
          const SizedBox(height: 32),
          LiveButton.hungUp(onTap: () => widget.onHangUp?.call(true)),
        ],
      );
    }
    return const SizedBox.shrink();
  }

  bool get isVideo => widget.callType == CallType.video;

  bool get isCalling => _callState == CallState.calling;
}
