import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:fis_common/logger/logger.dart';
import 'package:image/image.dart' as img;

import 'package:daniulive/controllers/merge_play_controller.dart';
import 'package:fis_lib_media_rt/base/capturer.dart';
import 'package:fis_lib_media_rt/implementations/nt_rtmp/channel/channel.dart';
import 'package:fis_lib_media_rt/implementations/nt_rtmp/native_share.dart';
import 'package:fis_lib_media_rt/implementations/nt_rtmp/values.dart';
import 'package:flutter/widgets.dart';
import 'package:image_crop_plus/image_crop_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:vid/us/vid_us_image_data.dart';

import 'jpg_convert_thread.dart';

class NTCapturer extends BaseCapturer {
  /// 录制采样速率
  static const int _recordRate = 15;

  /// 移动端文件缓存路径
  String? _recordTempPath;

  /// 录制开始时间
  DateTime _recordStartTime = DateTime.now();

  /// 是否需要裁图
  bool _isNeedCrop = false;

  /// 裁图区域
  Rect _cropArea = const Rect.fromLTWH(0, 0, 2 / 3, 2 / 3);

  MergePlayController? controller;
  StreamSubscription<ui.Image>? _subscription;
  ui.Image? _image;
  ui.Image? _workingImage;
  List<ui.Image>? _cachedImages;
  final _jpegConvertThread = JpegConvertThread();

  NTCapturer({required super.channel}) {
    if (channel is NTPlayChannel) {
      final ntChannel = channel as NTPlayChannel;
      if (ntChannel.isDeviceMain) {
        controller = NTNativeShare.playControllers
            .get(MERGE_DEVICE_NATIVE_SIGN)! as MergePlayController;
      }
    }
  }

  @override
  Future<void> initCapturer({
    int? width,
    int? height,
    bool isNeedCrop = false,
    Rect? cropArea,
  }) async {
    _isNeedCrop = isNeedCrop;
    _cropArea = cropArea ?? _cropArea;
    frameWidth = width ?? 640;
    frameHeight = height ?? 480;
    if (frameWidth == 0 && frameHeight == 0) {
      print("📹初始化失败，请检查摄像头状态");
    } else {
      isInit = true;
    }
  }

  @override
  Future<VidUsImageData> captureFrame() async {
    _check();

    final completer = Completer<VidUsImageData>();

    _subscription = controller!.imageStream.stream.listen((e) async {
      _subscription!.cancel(); //获取到一帧，停止监听
      _subscription = null;

      final bytes = await e.toByteData();
      img.Image bitmap = img.Image.fromBytes(
        e.width,
        e.height,
        bytes!.buffer.asUint8List(),
      );

      /// 创建空的 vid (默认10帧)
      VidUsImageData imageData =
          VidUsImageData.empty(10, VidUsImageFormat.Jpeg);

      await _saveBitmap2Vid(imageData, bitmap);

      completer.complete(imageData);
    });
    return completer.future;
  }

  @override
  Future<String> captureFrameAsBase64() async {
    _check();
    final completer = Completer<String>();
    _subscription = controller!.imageStream.stream.listen((e) async {
      _subscription!.cancel(); //获取到一帧，停止监听
      _subscription = null;
      final bytes = await e.toByteData();
      img.Image bitmap = img.Image.fromBytes(
        e.width,
        e.height,
        bytes!.buffer.asUint8List(),
      );
      final jpgBytes = Uint8List.fromList(img.encodeJpg(bitmap, quality: 80));
      String base64 = base64Encode(jpgBytes);
      completer.complete(base64);
    });
    return completer.future;
  }

  @override
  void startRecord() async {
    if (!isInit) return;
    if (isRecording) return;

    _recordTempPath = await getFileStorageDir();
    if (_recordTempPath == null) return;

    isRecording = true;
    _cachedImages = [];
    _recordStartTime = DateTime.now();
    const int captureInterval = 1000 ~/ _recordRate;
    const caotureDuration = Duration(milliseconds: captureInterval);

    _startListenFrames();

    while (isRecording) {
      final duration = await _cacheFrameFileOnce();
      final leftDuration = caotureDuration - duration;
      if (!leftDuration.isNegative) {
        print("Wait duration ${leftDuration.inMilliseconds}");
        await Future.delayed(leftDuration);
      }
    }
  }

  @override
  Future<VidUsImageData> stopRecord() async {
    if (!isRecording) return Future.error('Record not start');

    _stopListenFrames();
    isRecording = false;
    final duration = DateTime.now().difference(_recordStartTime).abs();

    /// 等待1s
    // 截图结束一秒后执行，保证所有截图都已保存
    await Future.delayed(const Duration(milliseconds: 1000));
    try {
      VidUsImageData vidBytes =
          await _createVidFromPath(_recordTempPath!, duration.inMilliseconds);
      return vidBytes;
    } catch (e) {
      rethrow;
    } finally {
      _cachedImages!.clear();
      _cachedImages = null;
      _cleanCache();
    }
  }

  @override
  void dispose() {}

  /// 获取保存路径下的所有 .dat 文件，返回 vid
  Future<VidUsImageData> _createVidFromPath(
      String path, int recordDurationMS) async {
    final Directory dir = Directory(path);
    final List<FileSystemEntity> list = dir.listSync();
    VidUsImageData imageData = VidUsImageData.empty(1, VidUsImageFormat.Jpeg);

    // await _jpegConvertThread.run();

    int imagesCount = 0;
    for (var item in _cachedImages!) {
      final success = await _readFrameFile2Vid(imageData, item);
      if (success) {
        imagesCount++;
      }
    }
    // _jpegConvertThread.cancel();

    int fps = (imagesCount * 1000 / recordDurationMS).round();
    imageData.setThirdPartFPS(fps);
    print("✨录制结束 📹总录制时长 $recordDurationMS 获取的图片数量 $imagesCount 计算得实际帧率 $fps");
    return imageData;
  }

  Future<bool> _readFrameFile2Vid(
      VidUsImageData imageData, ui.Image frame) async {
    try {
      final buffer = await frame.toByteData();
      final bitmap = img.Image.fromBytes(
          frame.width, frame.height, buffer!.buffer.asUint8List());
      final startTime = DateTime.now();
      await _saveBitmap2Vid(imageData, bitmap);
      final duration = DateTime.now().difference(startTime);
      print("!!!!!_readFrameFile2Vid spend ${duration.inMilliseconds}");
      return true;
    } catch (e) {
      logger.e("NTCapturer - read frame cache file 2 VidData fail.", e);
      return false;
    }
  }

  Future<void> _saveBitmap2Vid(
      VidUsImageData imageData, img.Image bitmap) async {
    int dstWidth = bitmap.width;
    int dstHeight = bitmap.height;

    if (_isNeedCrop) {
      dstWidth = (dstWidth * _cropArea.width).toInt();
      dstHeight = (dstHeight * _cropArea.height).toInt();
      bitmap = img.copyCrop(bitmap, 0, 0, dstWidth, dstHeight);
    }

    // TODO: adjust quality
    final jpgBytes = Uint8List.fromList(img.encodeJpg(bitmap, quality: 80));

    imageData.addJpegImage(jpgBytes, dstWidth, dstHeight);
  }

  /// 先将图形先缓存到文件
  Future<Duration> _cacheFrameFileOnce() async {
    if (_image == null) return Duration.zero;

    final startTime = DateTime.now();
    _workingImage = _image;
    _cachedImages!.add(_workingImage!);

    final endTime = DateTime.now();
    final duration = endTime.difference(startTime).abs();
    print(
        "Cache frame at $endTime. ${_workingImage!.hashCode} Spend-${duration.inMilliseconds} size: ${_workingImage!.width}x${_workingImage!.height}");

    return duration;
  }

  void _startListenFrames() {
    _subscription = controller!.imageStream.stream.listen((e) {
      _image = e;
      print("Recived a frame at ${DateTime.now()}");
    });
  }

  void _stopListenFrames() {
    _subscription?.cancel();
    _subscription = null;
  }

  /// 递归清理所有包含 vid_capture 的文件夹
  Future<void> _cleanCache() async {
    String tempDir = await _getVidCaptureDir();
    final path = tempDir;
    await _deleteFilesWithPattern(path, "temp");
  }

  Future<String?> getFileStorageDir() async {
    String tempDir = await _getVidCaptureDir();
    String suffix = DateTime.now().millisecondsSinceEpoch.toString();
    final String parentPath = "${suffix}_vid_capture_temp";
    final String dirPath = "$tempDir/$parentPath";
    Directory(dirPath).createSync(recursive: true);
    return dirPath;
  }

  /// Vid Capture 专属临时目录
  Future<String> _getVidCaptureDir() async {
    Directory tempDir = await getTemporaryDirectory();
    return "${tempDir.path}/vid_capture";
  }

  Future<void> _deleteFilesWithPattern(String path, String pattern) async {
    final dir = Directory(path);
    if (await dir.exists()) {
      await for (var entity in dir.list(recursive: true, followLinks: false)) {
        if (entity is Directory) {
          if (entity.path.contains(pattern)) {
            await entity.delete(recursive: true);
          } else {
            await _deleteFilesWithPattern(entity.path, pattern);
          }
        }
      }
    }
  }

  void _check() {
    if (controller == null) {
      throw ArgumentError("Cannot find MergePlayController");
    }
  }
}
