import 'dart:async';
import 'dart:convert';
import 'package:camera/camera.dart';
import 'package:fis_jsonrpc/rpc.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:vitalapp/architecture/storage/storage.dart';
import 'package:vitalapp/architecture/utils/prompt_box.dart';
import 'package:vitalapp/managers/interfaces/cache.dart';
import 'package:fis_common/logger/logger.dart';
import 'package:vitalapp/rpc.dart';
import 'package:vitalapp/store/store.dart';
import 'dart:ui' as ui;
import 'index.dart';

class FacialRecognitionController extends GetxController
    with WidgetsBindingObserver {
  FacialRecognitionController();

  final state = FacialRecognitionState();

  List<CameraDescription> _cameras = <CameraDescription>[];
  List<CameraDescription> get cameras => _cameras;

  CameraController? kCameraController;
  double _minAvailableExposureOffset = 0.0;
  double _maxAvailableExposureOffset = 0.0;
  double _minAvailableZoom = 1.0;
  double _maxAvailableZoom = 1.0;
  double _currentScale = 1.0;
  double _baseScale = 1.0;

  // 屏幕上手指数量
  int pointers = 0;

  // 当前需要返回的身份信息
  IdCardInfoModel idCardInfo = IdCardInfoModel();

  /// 开始缩放
  void handleScaleStart(ScaleStartDetails details) {
    _baseScale = _currentScale;
  }

  /// 当前捕获帧的人脸列表
  List<Face> kFrameFacesResult = [];

  /// 当前捕获帧大小
  Size kFrameImageSize = Size.zero;

  /// 缩放更新
  Future<void> handleScaleUpdate(ScaleUpdateDetails details) async {
    // When there are not exactly two fingers on screen don't scale
    if (kCameraController == null || pointers != 2) {
      return;
    }

    // 屏蔽缩放
    // _currentScale = (_baseScale * details.scale)
    //     .clamp(_minAvailableZoom, _maxAvailableZoom);

    // await kCameraController!.setZoomLevel(_currentScale);
  }

  /// 修改对焦点 [暂不执行]
  void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) {
    if (kCameraController == null) {
      return;
    }

    final CameraController cameraController = kCameraController!;

    final Offset offset = Offset(
      details.localPosition.dx / constraints.maxWidth,
      details.localPosition.dy / constraints.maxHeight,
    );
    cameraController.setExposurePoint(offset);
    cameraController.setFocusPoint(offset);
  }

  /// 初始化相机
  Future<void> initAvailableCameras() async {
    try {
      _cameras = await availableCameras();
      if (_cameras.isNotEmpty) {
        // state.isCameraReady = true;
      }
      // print("cameras: ${_cameras.length}");
    } on CameraException catch (e) {
      logger.e("cameras: ${e.code} ${e.description}");
    }
  }

  /// 启动指定相机
  Future<void> openNewCamera(CameraDescription cameraDescription) async {
    final CameraController? oldController = kCameraController;
    if (oldController != null) {
      // `kCameraController` needs to be set to null before getting disposed,
      // to avoid a race condition when we use the kCameraController that is being
      // disposed. This happens when camera permission dialog shows up,
      // which triggers `didChangeAppLifecycleState`, which disposes and
      // re-creates the kCameraController.
      kCameraController = null;
      await oldController.dispose();
    }

    final CameraController cameraController = CameraController(
      cameraDescription,
      ResolutionPreset.max,
      enableAudio: false,
      imageFormatGroup: ImageFormatGroup.jpeg,
    );

    kCameraController = cameraController;

    // If the kCameraController is updated then update the UI.
    cameraController.addListener(() {
      if (cameraController.value.hasError) {
        PromptBox.toast(
            "Camera error ${cameraController.value.errorDescription}");
      }
    });

    try {
      await cameraController.initialize();
      await Future.wait(<Future<Object?>>[
        // The exposure mode is currently not supported on the web.
        ...!kIsWeb
            ? <Future<Object?>>[
                cameraController.getMinExposureOffset().then(
                    (double value) => _minAvailableExposureOffset = value),
                cameraController
                    .getMaxExposureOffset()
                    .then((double value) => _maxAvailableExposureOffset = value)
              ]
            : <Future<Object?>>[],
        cameraController
            .getMaxZoomLevel()
            .then((double value) => _maxAvailableZoom = value),
        cameraController
            .getMinZoomLevel()
            .then((double value) => _minAvailableZoom = value),
      ]);
    } on CameraException catch (e) {
      switch (e.code) {
        case 'CameraAccessDenied':
          PromptBox.toast('You have denied camera access.');
          break;
        case 'CameraAccessDeniedWithoutPrompt':
          // iOS only
          PromptBox.toast('Please go to Settings app to enable camera access.');
          break;
        case 'CameraAccessRestricted':
          // iOS only
          PromptBox.toast('Camera access is restricted.');
          break;
        case 'AudioAccessDenied':
          PromptBox.toast('You have denied audio access.');
          break;
        case 'AudioAccessDeniedWithoutPrompt':
          // iOS only
          PromptBox.toast('Please go to Settings app to enable audio access.');
          break;
        case 'AudioAccessRestricted':
          // iOS only
          PromptBox.toast('Audio access is restricted.');
          break;
        default:
          PromptBox.toast('Error: ${e.code}\n${e.description}');
          break;
      }
    }
  }

  /// 遍历当前相机列表并启动后置相机
  void openBackCamera() async {
    if (_cameras.isEmpty) {
      PromptBox.toast('Error: No cameras found.');
    } else {
      for (CameraDescription cameraDescription in _cameras) {
        if (cameraDescription.lensDirection == CameraLensDirection.back) {
          await openNewCamera(cameraDescription);
          lockCaptureOrientation();
          state.isUsingFrontCamera = false;
          update();
          state.isCameraReady = true;
          break;
        }
      }
    }
  }

  /// 遍历当前相机列表并启动前置相机
  void openFrontCamera() async {
    if (_cameras.isEmpty) {
      PromptBox.toast('Error: No cameras found.');
    } else {
      for (CameraDescription cameraDescription in _cameras) {
        if (cameraDescription.lensDirection == CameraLensDirection.front) {
          await openNewCamera(cameraDescription);
          state.isUsingFrontCamera = true;
          lockCaptureOrientation();
          update();
          state.isCameraReady = true;
          break;
        }
      }
    }
  }

  /// 相机锁定旋转
  Future<void> lockCaptureOrientation() async {
    final CameraController? cameraController = kCameraController;
    if (cameraController == null || !cameraController.value.isInitialized) {
      PromptBox.toast('Error: select a camera first.');
      return;
    }

    if (!cameraController.value.isCaptureOrientationLocked) {
      try {
        await cameraController
            .lockCaptureOrientation(DeviceOrientation.landscapeLeft);
      } on CameraException catch (e) {
        PromptBox.toast('Error: ${e.code}\n${e.description}');
      }
    } else {
      PromptBox.toast('Rotation lock is already enabled.');
    }
  }

  /// 执行一次拍摄
  Future<XFile?> takePicture() async {
    final CameraController? cameraController = kCameraController;
    if (cameraController == null || !cameraController.value.isInitialized) {
      PromptBox.toast('Error: select a camera first.');
      return null;
    }

    if (cameraController.value.isTakingPicture) {
      // A capture is already pending, do nothing.
      return null;
    }

    try {
      final XFile file = await cameraController.takePicture();
      return file;
    } on CameraException catch (e) {
      PromptBox.toast('Error: ${e.code}\n${e.description}');
      return null;
    }
  }

  /// 测试图像文件缓存，print 遍历输出
  void debugShowCache() async {
    final cacheManager = Get.find<ICacheManager>();
    double cacheSize = await cacheManager.getCacheSize();
    double imageCacheSize = await cacheManager.getImageCacheSize();
    debugPrint('cacheSize = $cacheSize : ${formatSize(cacheSize)}');
    debugPrint(
        'imageCacheSize = $imageCacheSize : ${formatSize(imageCacheSize)}');
  }

  /// 文件大小转为可读 Str
  static String formatSize(double value) {
    List<String> unitArr = ['B', 'K', 'M', 'G'];
    int index = 0;
    while (value > 1024) {
      index++;
      value = value / 1024;
    }
    String size = value.toStringAsFixed(2);
    return size + unitArr[index];
  }

  /// 保存到相册
  void saveImageToGallery(XFile image) async {
    // 获取图像的字节数据
    Uint8List bytes = await image.readAsBytes();

    // 将图像保存到相册
    await ImageGallerySaver.saveImage(bytes, quality: 100);
  }

  /// 处理图像裁切
  Future<String> clipLocalImage(XFile soureceImage, double scale) async {
    assert(scale >= 1, 'scale must be greater than 1');
    // 获取图像的字节数据
    Uint8List bytes = await soureceImage.readAsBytes();
    var codec = await ui.instantiateImageCodec(bytes);
    var nextFrame = await codec.getNextFrame();
    var image = nextFrame.image;
    Rect src = Rect.fromLTWH(
      (scale - 1) / 2 / scale * image.width.toDouble(),
      (scale - 1) / 2 / scale * image.height.toDouble(),
      image.width.toDouble() / scale,
      image.height.toDouble() / scale,
    );
    Rect dst =
        Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble());
    ui.Image croppedImage = await getCroppedImage(image, src, dst);
    ByteData? newImageBytes =
        await croppedImage.toByteData(format: ui.ImageByteFormat.png);
    if (newImageBytes == null) {
      return '';
    }
    Uint8List newImageUint8List = newImageBytes.buffer.asUint8List();

    /// FIXME 不要存到相册而是存到临时目录
    Map<Object?, Object?> result =
        await ImageGallerySaver.saveImage(newImageUint8List, quality: 100);
    String jsonString = jsonEncode(result);
    Map<String, dynamic> json = jsonDecode(jsonString);
    String? filePath = json['filePath'];
    return filePath ?? '';
  }

  /// 获取图像文件的图像尺寸
  Future<Size> getImageSize(XFile soureceImage) async {
    // 获取图像的字节数据
    Uint8List bytes = await soureceImage.readAsBytes();
    var codec = await ui.instantiateImageCodec(bytes);
    var nextFrame = await codec.getNextFrame();
    var image = nextFrame.image;
    return Size(image.width.toDouble(), image.height.toDouble());
  }

  /// 获取裁切后的图像
  Future<ui.Image> getCroppedImage(ui.Image image, Rect src, Rect dst) {
    var pictureRecorder = ui.PictureRecorder();
    Canvas canvas = Canvas(pictureRecorder);
    canvas.drawImageRect(image, src, dst, Paint());
    return pictureRecorder.endRecording().toImage(
          dst.width.floor(),
          dst.height.floor(),
        );
  }

  /// 发生开始人脸识别事件
  void onCaptureFaceButtonPressed() {
    // runDetectionTimer();
    /// 接口测试
    rpcTest2();
  }

  void rpcTest() async {
    if (kCameraController == null) {
      return;
    }
    final XFile? file = await takePicture();
    if (file != null) {
      faceDetector = FaceDetector(options: FaceDetectorOptions());
      // faceDetector =
      //     FaceDetector(options: FaceDetectorOptions(enableContours: true));
      int faceNum =
          await doDetection(faceDetector, file.path); // max 分辨率下检测用时大约 100ms

      if (faceNum == 0) {
        PromptBox.toast('请将面部保持在识别框内');
        return;
      } else if (faceNum > 1) {
        PromptBox.toast('请保持只有一张面部在识别范围内');
        return;
      }

      /// TODO 上传图像到云然后传给后端
      final url = await rpc.storage.upload(file);
      print('⭐⭐⭐⭐⭐⭐⭐⭐ url: $url');
      try {
        PatientBaseDTO result =
            await rpc.patient.getPatientBaseByFaceImageAsync(
          GetPatientBaseByFaceImageRequest(
            token: Store.user.token,
            image: url,
          ),
        );
        print(result);
        if (result.faceScanErrorType == FaceScanErrorTypeEnum.Success) {
          PromptBox.toast('人脸识别成功，身份证号 ${result.cardNo}}');
          finishFaceDetection(result);
        } else {
          // 暂先将 API 错误和 查无此人都归为识别错误
          PromptBox.toast('人脸识别失败，请重试');
        }
      } catch (e) {
        logger.e("getPatientBaseByFaceImageAsync failed: $e", e);
      }

      // if (timer.tick == 1 || kFrameImageSize == Size.zero) {
      //   Size imageSize = await getImageSize(file);
      //   kFrameImageSize = imageSize;
      // }
      // int kTime = DateTime.now().millisecondsSinceEpoch;
      // print('⭐⭐⭐⭐⭐⭐⭐⭐ capture time: ${kTime - lastCaptureTime} ms');
      // lastCaptureTime = kTime;

      // /// 记录用时 ms

      // int endTime = DateTime.now().millisecondsSinceEpoch;
      // print('⭐⭐⭐⭐⭐⭐⭐⭐ detection time: ${endTime - lastCaptureTime} ms');
      // update(['face_bounding_box']);
      // if (timer.tick >= 10) {
      //   finishFaceDetection(); // TODO 接入真实的判断条件
      // }
    }
  }

  /// 人脸录入测试
  void rpcTest2() async {
    if (kCameraController == null) {
      return;
    }
    final XFile? file = await takePicture();
    if (file != null) {
      faceDetector = FaceDetector(options: FaceDetectorOptions());
      // faceDetector =
      //     FaceDetector(options: FaceDetectorOptions(enableContours: true));
      int faceNum =
          await doDetection(faceDetector, file.path); // max 分辨率下检测用时大约 100ms

      if (faceNum == 0) {
        PromptBox.toast('请将面部保持在识别框内');
        return;
      } else if (faceNum > 1) {
        PromptBox.toast('请保持只有一张面部在识别范围内');
        return;
      }

      /// TODO 上传图像到云然后传给后端
      final url = await rpc.storage.upload(file);
      print('⭐⭐⭐⭐⭐⭐⭐⭐ url: $url');
      try {
        bool result = await rpc.patient.savePatientBaseByFaceImageAsync(
          SavePatientBaseByFaceImageRequest(
            cardNo: '320520200006190612',
            token: Store.user.token,
            image: url,
          ),
        );
        print(result);
        if (result) {
          PromptBox.toast('人脸数据存入成功');
        } else {
          PromptBox.toast('人脸数据存入失败');
        }
      } catch (e) {
        logger.e("savePatientBaseByFaceImageAsync failed: $e", e);
      }

      // if (timer.tick == 1 || kFrameImageSize == Size.zero) {
      //   Size imageSize = await getImageSize(file);
      //   kFrameImageSize = imageSize;
      // }
      // int kTime = DateTime.now().millisecondsSinceEpoch;
      // print('⭐⭐⭐⭐⭐⭐⭐⭐ capture time: ${kTime - lastCaptureTime} ms');
      // lastCaptureTime = kTime;

      // /// 记录用时 ms

      // int endTime = DateTime.now().millisecondsSinceEpoch;
      // print('⭐⭐⭐⭐⭐⭐⭐⭐ detection time: ${endTime - lastCaptureTime} ms');
      // update(['face_bounding_box']);
      // if (timer.tick >= 10) {
      //   finishFaceDetection(); // TODO 接入真实的判断条件
      // }
    }
  }

  /// 发生结束录制视频事件
  void onStopButtonPressed() {
    stopVideoRecording().then((XFile? file) {
      if (file != null) {
        PromptBox.toast('Video recorded to ${file.path}');
        // videoFile = file;
        // _startVideoPlayer();
      }
      update();
    });
  }

  /// 发生开始录制视频事件
  void onVideoRecordButtonPressed() {
    startVideoRecording().then((_) {
      update();
    });
  }

  /// 暂停录制视频
  void onPauseButtonPressed() {
    pauseVideoRecording().then((_) {
      update();
    });
  }

  /// 恢复视频录制
  void onResumeButtonPressed() {
    resumeVideoRecording().then((_) {
      update();
    });
  }

  /// 开始录制视频
  Future<void> startVideoRecording() async {
    final CameraController? cameraController = kCameraController;

    if (cameraController == null || !cameraController.value.isInitialized) {
      PromptBox.toast('Error: select a camera first.');
      return;
    }

    if (cameraController.value.isRecordingVideo) {
      // A recording is already started, do nothing.
      return;
    }

    try {
      await cameraController.startVideoRecording();
    } on CameraException catch (e) {
      PromptBox.toast('Error: ${e.code}\n${e.description}');
      return;
    }
  }

  /// 停止录制视频
  Future<XFile?> stopVideoRecording() async {
    final CameraController? cameraController = kCameraController;

    if (cameraController == null || !cameraController.value.isRecordingVideo) {
      return null;
    }

    try {
      return cameraController.stopVideoRecording();
    } on CameraException catch (e) {
      PromptBox.toast('Error: ${e.code}\n${e.description}');

      return null;
    }
  }

  /// 暂停录制视频
  Future<void> pauseVideoRecording() async {
    final CameraController? cameraController = kCameraController;

    if (cameraController == null || !cameraController.value.isRecordingVideo) {
      return;
    }

    try {
      await cameraController.pauseVideoRecording();
    } on CameraException catch (e) {
      PromptBox.toast('Error: ${e.code}\n${e.description}');

      rethrow;
    }
  }

  /// 恢复视频录制
  Future<void> resumeVideoRecording() async {
    final CameraController? cameraController = kCameraController;

    if (cameraController == null || !cameraController.value.isRecordingVideo) {
      return;
    }

    try {
      await cameraController.resumeVideoRecording();
    } on CameraException catch (e) {
      PromptBox.toast('Error: ${e.code}\n${e.description}');

      rethrow;
    }
  }

  /// 在 widget 内存中分配后立即调用。
  @override
  void onInit() async {
    // await initCamera();
    super.onInit();
    WidgetsBinding.instance.addObserver(this);
  }

  /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
  @override
  void onReady() async {
    super.onReady();
    await initAvailableCameras();
    openFrontCamera();
  }

  /// 在 [onDelete] 方法之前调用。
  @override
  void onClose() {
    super.onClose();
    final cacheManager = Get.find<ICacheManager>();
    cacheManager.clearApplicationImageCache();
    closeDetector();
    WidgetsBinding.instance.removeObserver(this);
    final CameraController? cameraController = kCameraController;
    if (cameraController != null) {
      kCameraController = null;
      cameraController.dispose();
    }
  }

  /// dispose 释放内存
  @override
  void dispose() {
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) async {
    super.didChangeAppLifecycleState(state);
    final CameraController? cameraController = kCameraController;

    // App state changed before we got the chance to initialize.
    if (cameraController == null || !cameraController.value.isInitialized) {
      return;
    }

    if (state == AppLifecycleState.inactive) {
      cameraController.dispose();
      this.state.isCameraReady = false;
    } else if (state == AppLifecycleState.resumed) {
      await openNewCamera(cameraController.description);
      this.state.isCameraReady = true;
    }
  }

  /// 完成人脸识别
  void finishFaceDetection(PatientBaseDTO patient) {
    final result = FaceRecognitionResult(
      success: true,
      cardNo: idCardInfo.idCardNumber,
      name: idCardInfo.idCardName,
      nation: idCardInfo.idCardNation,
      gender:
          idCardInfo.idCardGender == '男' ? GenderEnum.Male : GenderEnum.Female,
      birthday: DateTime.now(),
      address: idCardInfo.idCardAddress,
    );
    // Get.back<FaceRecognitionResult>(
    //   result: result,
    // );
    // TODO 识别成功后的切换逻辑
  }

  /// WIP
  /// 面部识别 基于 Google's ML Kit
  ///

  InputImage inputImage = InputImage.fromFilePath('');
  FaceDetector faceDetector = FaceDetector(options: FaceDetectorOptions());

  // 进行一次人脸检测 (返回人脸数量)
  Future<int> doDetection(
    FaceDetector faceDetector,
    String imagePath,
  ) async {
    inputImage = InputImage.fromFilePath(imagePath);
    // inputImage = image;
    final List<Face> faces = await faceDetector.processImage(inputImage);
    kFrameFacesResult = [];
    kFrameFacesResult.addAll(faces);

    // for (Face face in faces) {
    //   final Rect boundingBox = face.boundingBox;

    //   final double? rotX =
    //       face.headEulerAngleX; // Head is tilted up and down rotX degrees
    //   final double? rotY =
    //       face.headEulerAngleY; // Head is rotated to the right rotY degrees
    //   final double? rotZ =
    //       face.headEulerAngleZ; // Head is tilted sideways rotZ degrees

    //   // If landmark detection was enabled with FaceDetectorOptions (mouth, ears,
    //   // eyes, cheeks, and nose available):
    //   final FaceLandmark? leftEar = face.landmarks[FaceLandmarkType.leftEar];
    //   if (leftEar != null) {
    //     final Point<int> leftEarPos = leftEar.position;
    //   }

    //   // If classification was enabled with FaceDetectorOptions:
    //   if (face.smilingProbability != null) {
    //     final double? smileProb = face.smilingProbability;
    //   }

    //   // If face tracking was enabled with FaceDetectorOptions:
    //   if (face.trackingId != null) {
    //     final int? id = face.trackingId;
    //   }
    // }
    return kFrameFacesResult.length;
  }

  // bool isDetectionRunning = false;
  Timer? _detectionTimer;

  /// 开始持续检测人脸
  void runDetectionTimer() {
    if (_detectionTimer != null) {
      _detectionTimer!.cancel();
      _detectionTimer = null;
      faceDetector.close();
      state.isShowFaceRecognitionResult = false;
      return;
    }
    faceDetector =
        FaceDetector(options: FaceDetectorOptions(enableContours: true));
    state.isShowFaceRecognitionResult = true;

    /// 记录最后一次拍摄的时间
    int lastCaptureTime = DateTime.now().millisecondsSinceEpoch;
    _detectionTimer = Timer.periodic(
      const Duration(milliseconds: 300), // max 分辨率下拍摄用时大约 500ms-800ms
      (timer) async {
        if (kCameraController == null) {
          return;
        }
        final XFile? file = await takePicture();
        if (file != null) {
          if (timer.tick == 1 || kFrameImageSize == Size.zero) {
            Size imageSize = await getImageSize(file);
            kFrameImageSize = imageSize;
          }
          int kTime = DateTime.now().millisecondsSinceEpoch;
          print('⭐⭐⭐⭐⭐⭐⭐⭐ capture time: ${kTime - lastCaptureTime} ms');
          lastCaptureTime = kTime;

          /// 记录用时 ms
          await doDetection(faceDetector, file.path); // max 分辨率下检测用时大约 100ms
          int endTime = DateTime.now().millisecondsSinceEpoch;
          print('⭐⭐⭐⭐⭐⭐⭐⭐ detection time: ${endTime - lastCaptureTime} ms');
          update(['face_bounding_box']);
          if (timer.tick >= 10) {
            // finishFaceDetection(); // TODO 接入真实的判断条件
          }
        }
      },
    );
  }

  /// 用于将读取的视频流传给 Google ML
  InputImage cameraImageToInputImage(CameraImage cameraImage) {
    return InputImage.fromBytes(
      bytes: _concatenatePlanes(cameraImage.planes),
      metadata: InputImageMetadata(
        size: Size(cameraImage.width.toDouble(), cameraImage.height.toDouble()),
        rotation: InputImageRotation.rotation0deg,
        format: _getInputImageFormat(cameraImage.format.group),
        bytesPerRow: cameraImage.planes[0].bytesPerRow,
      ),
    );
  }

  /// 辅助函数，将CameraImage的plane组合为Uint8List格式
  Uint8List _concatenatePlanes(List<Plane> planes) {
    final WriteBuffer allBytes = WriteBuffer();
    for (Plane plane in planes) {
      allBytes.putUint8List(plane.bytes);
    }
    return allBytes.done().buffer.asUint8List();
  }

  InputImageFormat _getInputImageFormat(ImageFormatGroup format) {
    switch (format) {
      case ImageFormatGroup.yuv420:
        return InputImageFormat.yuv420;
      case ImageFormatGroup.bgra8888:
        return InputImageFormat.bgra8888;
      default:
        throw ArgumentError('Invalid image format');
    }
  }

  /// 销毁检测器
  void closeDetector() {
    if (_detectionTimer != null) {
      state.isShowFaceRecognitionResult = false;
      _detectionTimer!.cancel();
      _detectionTimer = null;
    }
    faceDetector.close();
  }
}

class FaceRecognitionResult {
  bool success;

  /// 身份证号
  String cardNo;

  /// 姓名
  String name;

  /// 性别
  GenderEnum gender;

  /// 民族
  String nation;

  /// 出生日期
  DateTime birthday;

  /// 地址
  String address;

  FaceRecognitionResult({
    required this.success,
    required this.cardNo,
    required this.name,
    required this.gender,
    required this.nation,
    required this.birthday,
    required this.address,
  });
}
