import 'dart:async';
import 'dart:io';
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:flutter_smartscan_plugin/id_card_recognition.dart';
import 'package:get/get.dart';
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
import 'package:intl/intl.dart';
import 'package:vitalapp/architecture/storage/storage.dart';
import 'package:vitalapp/architecture/utils/common_util.dart';
import 'package:vitalapp/architecture/utils/prompt_box.dart';
import 'package:vitalapp/architecture/values/features.dart';
import 'package:vitalapp/global.dart';
import 'package:vitalapp/managers/interfaces/cache.dart';
import 'package:fis_common/logger/logger.dart';
import 'package:vitalapp/managers/interfaces/patient.dart';
import 'package:vitalapp/rpc.dart';
import 'package:vitalapp/store/store.dart';
import 'index.dart';
import 'dart:ui' as ui;

class IdCardScanController extends GetxController with WidgetsBindingObserver {
  IdCardScanController();

  final state = IdCardScanState();
  List<CameraDescription> _cameras = <CameraDescription>[];
  List<CameraDescription> get cameras => _cameras;
  // final idCardRecognition = IDCardRecognition();

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

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

  /// 开始缩放
  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();
          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);
          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;
    }
  }

  /// 发生拍摄身份证事件
  void onCaptureIdCardButtonPressed() {
    state.isIdCardScanning = true;
    state.processingImageLocalPath = '';
    takePicture().then((XFile? file) async {
      if (file != null) {
        state.processingImageLocalPath = file.path;
        try {
          PatientBaseDTO result;

          ///具有权限或者离线情况使用本地离线身份证识别包
          if (Store.user.hasFeature(FeatureKeys.IdCardOfflineRecognition)) {
            File fileIDCard = File(file.path);
            List<int> bytes = await fileIDCard.readAsBytes();
            ui.Codec codec =
                await ui.instantiateImageCodec(Uint8List.fromList(bytes));

            ui.FrameInfo frameInfo = await codec.getNextFrame();
            ui.Image image = frameInfo.image;
            // 计算裁剪区域  身份证尺寸 85.6毫米×54毫米
            if (image.width > 856 && image.height > 540) {
              final pictureRecorder = ui.PictureRecorder();
              final canvas = Canvas(pictureRecorder);
              const srcRect = Rect.fromLTWH(532, 270, 856, 540);
              const dstRect = Rect.fromLTWH(0, 0, 856, 540);
              canvas.drawImageRect(image, srcRect, dstRect, Paint());
              final picture = pictureRecorder.endRecording();
              image = await picture.toImage(856, 540);
            }
            ByteData? byteData = await image.toByteData();
            state.processingImageUint8List = byteData!.buffer.asUint8List();
            logger.i("getPatientBaseByImageAsync evaluateOneImage start");
            final idCardRecogResultInfo =
                await CommonUtil.idCardRecognition.evaluateOneImage(image);
            if (idCardRecogResultInfo != null) {
              logger.i(
                  "getPatientBaseByImageAsync idCardRecogResultInfo.numerStatus: ${idCardRecogResultInfo.numerStatus}");
            } else {
              logger.e(
                  "getPatientBaseByImageAsync CommonUtil.idCardRecognition.evaluateOneImage fial!");
            }
            if (idCardRecogResultInfo != null &&
                idCardRecogResultInfo.numerStatus == 1) {
              String formattedDateString = idCardRecogResultInfo.birthdate!
                  .replaceAll('年', '-')
                  .replaceAll('月', '-')
                  .replaceAll('日', '');
              DateFormat format = DateFormat('yyyy-MM-dd');
              DateTime birthday = format.parse(formattedDateString);
              result = PatientBaseDTO(
                isSuccess: true,
                cardNo: idCardRecogResultInfo.idNumber,
                patientName: idCardRecogResultInfo.name,
                patientAddress: idCardRecogResultInfo.address,
                patientGender: idCardRecogResultInfo.gender == "女"
                    ? GenderEnum.Female
                    : GenderEnum.Male,
                nationality: idCardRecogResultInfo.nation,
                birthday: birthday,
              );
            } else {
              result = PatientBaseDTO(
                  isSuccess: false, errorMessage: "身份证识别失败，请保持图像清晰完整");
            }
            // CommonUtil.idCardRecognition.release();
          } else {
            final String fileType = file.path.split('.').last;
            if (!['png', 'jpg'].contains(fileType)) {
              PromptBox.toast('上传的图像类型错误');
              return;
            }
            final url = await rpc.storage.upload(
              file,
              fileType: fileType,
            );
            print('⭐⭐⭐⭐⭐⭐⭐⭐ url: $url');
            if (url == null || url.isEmpty) {
              PromptBox.toast('图像上传超时，请检测网络');
              throw Exception('图像上传超时');
            }

            result = await rpc.patient.getPatientBaseByImageAsync(
              GetPatientBaseByImageRequest(
                token: Store.user.token,
                image: url,
              ),
            );
          }

          state.processingImageLocalPath = '';

          /// 用于关闭 ImageDetectingDialog
          if (result.isSuccess) {
            PromptBox.toast('身份证识别成功');
            final idCardScanResult = IdCardScanResult(
              success: true,
              patientBaseDTO: result,
            );

            Get.back<IdCardScanResult>(
              result: idCardScanResult,
            );
          } else {
            PromptBox.toast('身份证识别失败，请保持图像清晰完整');
          }
        } catch (e) {
          logger.e("getPatientBaseByImageAsync failed: $e", e);
        }
      }
      state.processingImageLocalPath = '';
      state.isIdCardScanning = false;
    });
  }

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

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

  /// 在 [onDelete] 方法之前调用。
  @override
  void onClose() {
    super.onClose();
    final cacheManager = Get.find<ICacheManager>();
    cacheManager.clearApplicationImageCache();
    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;
    }
  }
}

class IdCardScanResult {
  bool success;

  /// 身份证信息
  PatientBaseDTO patientBaseDTO;

  IdCardScanResult({
    required this.success,
    required this.patientBaseDTO,
  });
}
