import 'dart:async';
import 'dart:math';

import 'package:fis_common/event/event_type.dart';
import 'package:fis_common/index.dart';
import 'package:fis_i18n/i18n.dart';
import 'package:fis_jsonrpc/rpc.dart';
import 'package:fis_measure/utils/prompt_box.dart';
import 'package:fis_ui/index.dart';
import 'package:flutter/foundation.dart';
import 'package:flyinsono/architecture/router/index.dart';
import 'package:flyinsono/architecture/services/entity/local_setting.dart';
import 'package:flyinsono/architecture/services/modules/hardware_setting_configuration.dart';
import 'package:flyinsono/architecture/states/states.dart';
import 'package:flyinsono/rpc.dart';
import 'package:get/get.dart';
import 'package:universal_html/html.dart' as html;
import 'interfaces/entities/live_course.dart';
import 'interfaces/live_course.dart';
import 'base_manager.dart';
import 'live_course_listening_manager.dart';
import 'package:fis_common/logger/logger.dart';

class LiveCourseManager extends BaseManager implements ILiveCourseManager {
  final onJoinLiveCourse = FEventHandler<JoinLiveCourseNotification>();
  final onMute = FEventHandler<MuteLiveCourseNotification>();
  final onNetworkErr = FEventHandler<NetworkErrCourseNotification>();
  final onHeartRateLeave = FEventHandler<HeartRateLeaveCourseNotification>();
  final onHeartRateJoin = FEventHandler<HeartRateJoinCourseNotification>();
  final onLeave = FEventHandler<LeaveLiveCourseNotification>();
  final onCloseLiveCourse = FEventHandler<CloseLiveCourseNotification>();
  final onChangeShareInLiveCourse =
      FEventHandler<ChangeShareInLiveCourseNotification>();
  final onBoardDataReceive =
      FEventHandler<SendLiveInteractiveBoardDataNotification>();
  final onCourseStatusChange = FEventHandler<CourseStatusNotification>();

  final onSwitchVideoOpend = FEventHandler<SwitchLiveCourseVideoNotification>();
  final onProcessChange = FEventHandler<double>();
  final exitDesktopSharing = FEventHandler<bool>();

  late final LiveCourseListeningManager listeningManager;
  Timer? timer;
  LiveCourseManager() {
    listeningManager = LiveCourseListeningManager(this);
  }

  @override
  Future<CourseInfoDetailDTO> getCourseInfo(String code) async {
    final result = await rpc.education.findCourseByCodeAsync(
      FindCourseByCodeRequest(
        code: code,
        token: token,
        languageCode: i18nBook.locale.toCodeString('-'),
      ),
    );
    return result;
  }

  ///开启录制
  @override
  Future<void> startRecording(String courseCode) async {
    if (FPlatform.isMacOS || FPlatform.isWindows) {
      var courseDetail = await getCourseInfo(courseCode);
      var courseName = courseDetail.name ?? '';

      rpc.platform.startTrainningRecord(
          courseName, courseDetail.courseChannel!.rtmpPullUrl!);
    }
  }

  ///停止录制
  @override
  void stopRecording() {
    if (FPlatform.isMacOS || FPlatform.isWindows) {
      String fileStoragePath = LocalSetting.ins.liveRecordPath ?? "";
      rpc.platform.stopTrainningRecord(fileStoragePath);
    }
  }

  @override
  Future<LiveCourse> initiateLiveCourse(String code) async {
    final result = await rpc.education.initiateLiveCourseAsync(
      InitiateLiveCourseRequest(
        courseCode: code,
        token: token,
      ),
    );
    print('heartRateInterval:${result.heartRateInterval}');
    _startSendHeartbeat(code, result.heartRateInterval);
    return LiveCourse.fromInitDto(result);
  }

  @override
  Future<void> setShareInLiveCourse(
    String courseCode,
    String currentShareMemberCode, {
    CourseShareType currentShareType = CourseShareType.Window,
    CourseShareStates currentShareState = CourseShareStates.Opened,
  }) async {
    rpc.education.setShareInLiveCourseAsync(
      SetShareInLiveCourseRequest(
        token: token,
        courseCode: courseCode,
        currentShareMemberCode: currentShareMemberCode,
        currentShareType: currentShareType,
        currentShareState: currentShareState,
      ),
    );
  }

  @override
  Future<LiveCourse> joinLiveCourse(String code) async {
    var result = await rpc.education.joinLiveCourseAsync(JoinLiveCourseRequest(
      token: token,
      courseCode: code,
    ));
    _startSendHeartbeat(code, result.heartRateInterval);
    return LiveCourse.fromJoinDto(result);
  }

  @override
  Future<LiveCourseBaseResult> leaveLiveCourse(String code) async {
    timer?.cancel();
    timer = null;
    final result = await rpc.education.leaveLiveCourseAsync(
      LeaveLiveCourseRequest(
        courseCode: code,
        token: token,
      ),
    );
    return result;
  }

  @override
  Future<StudentLivePullUrlResult> getCoursePullUrl(String code) async {
    var result =
        await rpc.education.getCoursePullUrlAsync(InitiateLiveCourseRequest(
      courseCode: code,
      token: token,
    ));
    return result;
  }

  @override
  Future<LiveCourseMember> deviceJoinInLiveCourse(
      String deviceCode, String courseCode) async {
    var result =
        await rpc.education.deviceJoinInLiveCourseAsync(JoinLiveCourseRequest(
      deviceCode: deviceCode,
      courseCode: courseCode,
      token: Store.user.token,
    ));
    return result;
  }

  @override
  Future<void> openLiveCoursePage(String courseCode) async {
    var courseInfo = await getCourseInfo(courseCode);
    if (courseInfo.teacherCode == Store.user.code) {
      if (FPlatform.isMacOS || FPlatform.isWindows) {
        if (!LocalSetting.ins.isUseSecondWindow!) {
          router.to(
            RouteNames.College.LiveCourseDetail,
            parameters: {
              "courseCode": courseCode,
            },
          );
          return;
        } else {
          //老师开课强制开启第二窗口
          await Get.find<HardwareSettingService>().syncToShell();
          rpc.platform.openLiveCoursePage(courseCode, token);
          return;
        }
      } else {
        PromptBox.toast(i18nBook.college.teacherOnlyPC.t);
        if (!kDebugMode) {
          return;
        }
      }
    }
    if (FPlatform.isPureWeb && Store.app.enableNewTab) {
      final url = html.window.location.origin;
      var languageCode = i18nBook.locale.toCodeString("-");
      html.window.open(
        '$url/index.html?page=liveCourse&token=$token&courseCode=$courseCode&lagCode=$languageCode',
        '',
      );
    } else if ((FPlatform.isMacOS || FPlatform.isWindows) &&
        LocalSetting.ins.isUseSecondWindow!) {
      await Get.find<HardwareSettingService>().syncToShell();
      rpc.platform.openLiveCoursePage(courseCode, token);
    } else {
      router.to(
        RouteNames.College.LiveCourseDetail,
        id: kIsMobile ? null : NavIds.HOME,
        parameters: {"courseCode": courseCode},
      );
    }
  }

  ///设置开启或关闭静音状态
  @override
  Future<LiveCourseBaseResult> setMemberMute(
      String courseCode, bool isMute) async {
    return await rpc.education.setMuteLiveCourseAsync(SetMuteLiveCourseRequest(
      courseCode: courseCode,
      isMute: isMute,
      token: token,
    ));
  }

  @override
  Future<LiveCourseBaseResult> setVideoLiveCourse(
      String courseCode, bool isOpenVideo) async {
    return await rpc.education
        .setVideoLiveCourseAsync(SetVideoLiveCourseRequest(
      courseCode: courseCode,
      isOpened: isOpenVideo,
      token: token,
    ));
  }

  /// 开始发送心跳
  Future<void> _startSendHeartbeat(
      String courseCode, int intervalSeconds) async {
    final int safeIntervalSeconds =
        max(10, intervalSeconds); // 最小安全间隔时间为 10s 直播比较频繁
    final intervalMillseconds = 1000 * safeIntervalSeconds;
    if (timer != null) {
      logger.e("Last heartbeat timer cancelled");
      timer!.cancel();
      timer = null;
    }
    logger.e("Start heartbeat timer", courseCode);
    timer = Timer.periodic(
      Duration(milliseconds: intervalMillseconds),
      (timer) {
        _sendHeartbeat(courseCode);
      },
    );
    // 立即发送一次心跳
    _sendHeartbeat(courseCode);
  }

  Future<void> _sendHeartbeat(String code) async {
    try {
      // print("发送直播课心跳，课程 code: $code");
      logger.e("SendHeart course beat", code);
      await rpc.education.heartRateAsync(
        LiveHeartRateRequest(
          liveCode: code,
          token: token,
        ),
      );
    } on JsonRpcException catch (ex) {
      if (ex.code == 7031) {
        ///7031:课程已结束
        onCourseStatusChange.emit(
          this,
          CourseStatusNotification(
            courseCode: code,
            status: CourseStatusEnum.HasEnded,
          ),
        );
      }
    } catch (e) {}
  }
}
