import 'dart:async';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html' as html;

import 'package:fis_common/env/env.dart';
import 'package:fis_common/logger/logger.dart';
import 'package:fis_lib_media_rt/implementations/nt_rtmp/members.dart';
import 'package:fis_lib_media_rt/implementations/nt_rtmp/models.dart';
import 'package:fis_lib_media_rt/interface/errors.dart';
import 'package:flutter/widgets.dart';

import '../rtc_adapter.dart';
import '../shell_api.dart';
import '../values.dart';
import 'base.dart';

class NTPublishChannel extends NTBasePublishChannel {
  int _volume = 100;

  NTPublishChannel({required super.member});

  @override
  int get volume => _volume;

  @override
  void setAudioMute(bool isMute) async {
    if (!FPlatform.isPureWeb) {
      await ShellApi.setRtmpPublishAudioMute(isMute);
    }
    super.setAudioMute(isMute);
  }

  @override
  void setVideoMute(bool isMute) async {
    if (!FPlatform.isPureWeb) {
      await ShellApi.setRtmpPublishVideoMute(isMute);
    }
    super.setVideoMute(isMute);
  }

  @override
  void setVolume(int volume) async {
    if (!FPlatform.isPureWeb) {
      await ShellApi.setRtmpPublishVolume(volume);
    }
    _volume = volume;
  }

  @override
  void onViewIdChanged() {
    updateReadyState(viewId > 0);
  }
}

class NTPlayChannel extends NTBasePlayChannel {
  int _volume = 100;

  _IframeHook? _iframeHook;
  int _subDeviceViewId = 0;

  NTPlayChannel({required super.member});

  @override
  int get volume => _volume;

  @override
  void setAudioMute(bool isMute) async {
    await _iframeHook?.setAudioMute(isMute);
    super.setAudioMute(isMute);
  }

  @override
  void setVideoMute(bool isMute) async {
    await _iframeHook?.setVideoMute(isMute);
    super.setVideoMute(isMute);
  }

  @override
  Future<void> play() async {
    if (viewId < 0) {
      throw FRealTimeException(
        message:
            "PlayChannel-$id (userId: ${member.userId}) has not a valid view id",
      );
    }
    await _iframeHook?.play();

    return super.play();
  }

  @override
  Future<void> stop() async {
    await _iframeHook?.stop();
    return super.stop();
  }

  @override
  void setVolume(int volume) {
    _iframeHook?.setVolume(volume).then((_) {
      _volume = volume;
    });
  }

  @override
  void onViewIdChanged() {
    _iframeHook = _IframeHook(viewId: viewId);

    if (isDeviceMain && _subDeviceViewId > 0) {
      mountSubView(_subDeviceViewId);
    }
  }

  @override
  void onSubViewIdChanged() {
    final isMerge = (liveInfo as DeviceRtmpLiveInfo).isMerge;
    if (!isMerge) return;

    // 通知主画面
    final members = NTRtmp2RtcAdapter.adapter.room!.remoteMembers;
    for (final member in members) {
      final ntMember = member as NTRemoteMember;
      if (ntMember.isDevice) {
        final channel = ntMember.channels.first as NTBasePlayChannel;
        if (channel.isDeviceMain) {
          channel.mountSubView(viewId);
        }
      }
    }
  }

  @override
  void mountSubView(int subViewId) {
    _subDeviceViewId = subViewId;

    if ((liveInfo as DeviceRtmpLiveInfo).isMerge) {
      final elementId = "$IFRAME_ID_PREFIX$subViewId";
      WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
        // TODO: 500ms延迟用于等待子iframe加载完成，这里应该让子iframe在加载完成后通知挂载
        Future.delayed(const Duration(milliseconds: 500), () {
          // 通知主画面挂载裁图子画面
          _iframeHook?.mountSubView(elementId);
          // TODO: 在子iframe dispose后应该通知Unmount
        });
      });
    }
  }
}

class _IframeHook {
  final int viewId;

  _IframeHook({
    required this.viewId,
  }) {
    // _listenCallback();
  }

  Future<void> play() {
    return callMethod("play");
  }

  Future<void> stop() {
    return callMethod("stop");
  }

  Future<void> setVolume(int volume) {
    return callMethod("setVolume", volume);
  }

  Future<void> setAudioMute(bool isMute) {
    return callMethod("setAudioMute", isMute);
  }

  Future<void> setVideoMute(bool isMute) {
    return callMethod("setVideoMute", isMute);
  }

  Future<void> mountSubView(String elementId) {
    return callMethod("mountSubView", elementId);
  }

  Future<dynamic> callMethod(
    String methodName, [
    dynamic args,
  ]) async {
    final iframe = findIframe();
    if (iframe != null) {
      final completer = Completer<dynamic>();

      final message = <String, dynamic>{
        "method": methodName,
        "data": args,
      };

      iframe.contentWindow!.postMessage(message, "*");
      // TODO: mock wait message event. the right way is [_listenCallback]
      Future.delayed(
        const Duration(milliseconds: 8),
        () {
          completer.complete(123);
        },
      );
      return completer.future;
    } else {
      logger.w("$TAG -  iframe-$viewId is not found. ");
      throw Exception("[NTRMTP_RTC] iframe-$viewId is not found. ");
    }
  }

  void _listenCallback() {
    html.window.addEventListener(
      "message",
      (event) {
        dynamic e = event;
        final name = e.data['name'];
        final data = e.data['data'];
        print(name);
        print(data);
      },
    );
  }

  html.IFrameElement? findIframe() {
    return html.document.getElementById(getIframeId()) as html.IFrameElement?;
  }

  String getIframeId() {
    return "$IFRAME_ID_PREFIX$viewId";
  }
}
