import 'dart:io';
import 'dart:math' as Math;

import 'package:camera/camera.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:google_mlkit_face_mesh_detection/google_mlkit_face_mesh_detection.dart';
import 'package:sigap/src/features/auth/data/models/face_model.dart';
import 'package:sigap/src/features/auth/presentasion/controllers/selfie-verification/selfie_verification_controller.dart';
import 'package:uuid/uuid.dart';

enum LivenessStatus {
  preparing,
  detectingFace,
  checkLeftRotation,
  checkRightRotation,
  checkSmile,
  checkEyesOpen,
  readyForPhoto,
  photoTaken,
  completed,
  failed,
}

class FaceLivenessController extends GetxController
    with WidgetsBindingObserver {
  // Camera
  CameraController? _cameraController;
  late FaceDetector _faceDetector;
  var frontCamera;

  // Face Detection States
  final _isFaceInFrame = false.obs;
  final _isFaceLeft = false.obs;
  final _isFaceRight = false.obs;
  final _isEyeOpen = false.obs;
  final _isNoFace = false.obs;
  final _isMultiFace = false.obs;
  final _isCaptured = false.obs;
  final _isSmiled = false.obs;
  final _isFaceReadyForPhoto = false.obs;
  final _isDifferentPerson = false.obs;

  // Status tracking
  final Rx<LivenessStatus> status = Rx<LivenessStatus>(
    LivenessStatus.preparing,
  );
  final RxString currentInstruction = RxString('Initializing camera...');

  // Getters
  bool get isFaceInFrame => _isFaceInFrame.value;
  bool get isFaceLeft => _isFaceLeft.value;
  bool get isFaceRight => _isFaceRight.value;
  bool get isEyeOpen => _isEyeOpen.value;
  bool get isNoFace => _isNoFace.value;
  bool get isMultiFace => _isMultiFace.value;
  bool get isCaptured => _isCaptured.value;
  bool get isSmiled => _isSmiled.value;
  bool get isFaceReadyForPhoto => _isFaceReadyForPhoto.value;
  bool get isDifferentPerson => _isDifferentPerson.value;

  CameraController? get cameraController => _cameraController;

  // Face Mesh Detector
  final FaceMeshDetector _faceMeshDetector = FaceMeshDetector(
    option: FaceMeshDetectorOptions.faceMesh,
  );

  // Face Comparison
  List<double>? _firstPersonEmbedding;

  // Captured Image
  final _capturedImage = Rxn<XFile>();
  XFile? get capturedImage => _capturedImage.value;

  // Successful Steps
  final _successfulSteps = <String>[].obs;
  List<String> get successfulSteps => _successfulSteps;

  // Face Detector Options
  final FaceDetectorOptions options = FaceDetectorOptions(
    performanceMode:
        Platform.isAndroid ? FaceDetectorMode.fast : FaceDetectorMode.accurate,
    enableClassification: true,
    enableLandmarks: true,
    enableTracking: true,
  );

  // Device Orientations
  final orientations = {
    DeviceOrientation.portraitUp: 0,
    DeviceOrientation.landscapeLeft: 90,
    DeviceOrientation.portraitDown: 180,
    DeviceOrientation.landscapeRight: 270,
  };

  @override
  void onInit() {
    super.onInit();
    WidgetsBinding.instance.addObserver(this);

    // Lock orientation to portrait mode
    _lockDeviceOrientation();

    // Cek capabilities dulu
    _checkCameraCapabilities();

    // Coba inisialisasi dengan fallback
    _initializeCameraWithFallback().catchError((e) {
      print("❌ All camera initialization attempts failed: $e");
      status.value = LivenessStatus.failed;
      currentInstruction.value =
          'Camera initialization failed. Please restart the app.';
    });

    _faceDetector = FaceDetector(options: options);
  }

  // 1. UBAH KONFIGURASI CAMERA CONTROLLER
  Future<void> _initializeCamera() async {
    try {
      status.value = LivenessStatus.preparing;
      currentInstruction.value = 'Initializing camera...';

      final cameras = await availableCameras();
      final frontCameras = cameras.firstWhere(
        (camera) => camera.lensDirection == CameraLensDirection.front,
      );

      frontCamera = frontCameras;

      // ✅ PERBAIKAN UTAMA: Gunakan format yang didukung ML Kit
      _cameraController = CameraController(
        frontCamera,
        ResolutionPreset.medium, // Naikkan dari low ke medium
        imageFormatGroup:
            Platform.isAndroid
                ? ImageFormatGroup
                    .nv21 // ✅ PENTING: Gunakan NV21 untuk Android
                : ImageFormatGroup.bgra8888, // iOS tetap BGRA8888
        enableAudio: false,
      );

      await _cameraController!.initialize();

      // Tambahkan delay untuk stabilisasi
      await Future.delayed(Duration(milliseconds: 1000));

      await _cameraController!.setFlashMode(FlashMode.off);
      await _cameraController!.setFocusMode(FocusMode.auto);
      await _cameraController!.setExposureMode(ExposureMode.auto);

      print(
        "Camera initialized with resolution: ${_cameraController!.value.previewSize}",
      );

      _cameraController!.startImageStream((CameraImage img) {
        _processCameraImage(img);
      });

      status.value = LivenessStatus.detectingFace;
      currentInstruction.value = 'Position your face in the frame';
      update();
    } catch (e) {
      print('Error initializing camera: $e');
      status.value = LivenessStatus.failed;
      currentInstruction.value = 'Failed to initialize camera: $e';
    }
  }

  // Throttling flag to control processing rate
  bool _throttled = false;

  // Hapus batasan frame untuk deteksi wajah - selalu anggap wajah dalam frame
  void _handleFaceDetection(Face face) {
    if (!_isCaptured.value) {
      final double? rotY = face.headEulerAngleY;
      final double leftEyeOpen = face.leftEyeOpenProbability ?? -1.0;
      final double rightEyeOpen = face.rightEyeOpenProbability ?? -1.0;
      final double smileProb = face.smilingProbability ?? -1.0;

      print("HEAD DETECTION - Head angle: $rotY");
      print(
        "HEAD DETECTION - Eyes: L=$leftEyeOpen, R=$rightEyeOpen, Smile=$smileProb",
      );

      // Selalu anggap wajah sudah dalam frame jika wajah terdeteksi
      _updateFaceInFrameStatus();

      // Print detection state
      print("Detection state - Face in frame: ${_isFaceInFrame.value}");
      print("Detection state - Face left: ${_isFaceLeft.value}");
      print("Detection state - Face right: ${_isFaceRight.value}");
      print("Detection state - Smiled: ${_isSmiled.value}");
      print("Detection state - Eyes open: ${_isEyeOpen.value}");
      print("Detection state - Ready for photo: ${_isFaceReadyForPhoto.value}");

      _updateHeadRotationStatus(rotY);
      _updateSmilingStatus(smileProb);
      _updateEyeOpenStatus(leftEyeOpen, rightEyeOpen);
      _updateFaceInFrameForPhotoStatus(rotY, smileProb);

      // Log status updates
      print("Updated status: ${status.value}");
      print("Current instruction: ${currentInstruction.value}");

      if (_isFaceInFrame.value &&
          _isFaceLeft.value &&
          _isFaceRight.value &&
          _isSmiled.value &&
          _isFaceReadyForPhoto.value &&
          _isEyeOpen.value) {
        if (!_isCaptured.value) {
          _captureImage();
        }
      }
    }
  }

  // 2. PERBAIKAN INPUT IMAGE PROCESSING
  InputImage? _getInputImageFromCameraImage(CameraImage image) {
    try {
      final sensorOrientation = frontCamera.sensorOrientation;
      InputImageRotation? rotation;

      // Log format untuk debugging
      // print(
      //   "📸 Camera image format: ${image.format.raw}, planes: ${image.planes.length}",
      // );

      // Handle rotasi sesuai platform
      if (Platform.isIOS) {
        rotation =
            InputImageRotationValue.fromRawValue(sensorOrientation) ??
            InputImageRotation.rotation0deg;
      } else if (Platform.isAndroid) {
        var rotationCompensation =
            orientations[_cameraController!.value.deviceOrientation] ?? 0;

        if (frontCamera.lensDirection == CameraLensDirection.front) {
          rotationCompensation =
              (sensorOrientation + rotationCompensation) % 360;
        } else {
          rotationCompensation =
              (sensorOrientation - rotationCompensation + 360) % 360;
        }

        rotation =
            InputImageRotationValue.fromRawValue(rotationCompensation) ??
            InputImageRotation.rotation0deg;
      }

      // Deteksi format gambar
      final format = InputImageFormatValue.fromRawValue(image.format.raw);

      // Ukuran gambar
      final Size imageSize = Size(
        image.width.toDouble(),
        image.height.toDouble(),
      );

      // Tangani format YUV_420_888 yang terdeteksi di log debug
      if (image.format.raw == 35) {
        // YUV_420_888 memiliki raw value 35
        // print("🔄 Processing YUV_420_888 format (raw value: 35)");

        if (image.planes.length != 3) {
          // print(
          //   "❌ YUV_420_888 requires 3 planes but got: ${image.planes.length}",
          // );
          return null;
        }

        // Konversi YUV_420_888 ke format yang dimengerti ML Kit
        // Dengan mendapatkan hanya Y plane (luminance) yang bekerja untuk deteksi wajah
        final plane =
            image.planes[0]; // Y plane adalah yang penting untuk deteksi wajah

        return InputImage.fromBytes(
          bytes: plane.bytes,
          metadata: InputImageMetadata(
            size: imageSize,
            rotation: rotation ?? InputImageRotation.rotation0deg,
            format: InputImageFormat.nv21, // Format yang diharapkan ML Kit
            bytesPerRow: plane.bytesPerRow,
          ),
        );
      }
      // Format lain (NV21 atau BGRA8888)
      else if (format == InputImageFormat.nv21 ||
          format == InputImageFormat.bgra8888) {
        if (image.planes.isEmpty) {
          // print("❌ Not enough planes for ${format.toString()}");
          return null;
        }

        final plane = image.planes.first;

        return InputImage.fromBytes(
          bytes: plane.bytes,
          metadata: InputImageMetadata(
            size: imageSize,
            rotation: rotation ?? InputImageRotation.rotation0deg,
            format: InputImageFormat.nv21, // Format yang diharapkan ML Kit
            bytesPerRow: plane.bytesPerRow,
          ),
        );
      }
      // Format tidak dikenali
      else {
        // Mencoba konversi alternatif untuk format yang tidak didukung langsung
        return _processUnsupportedFormat(image, imageSize, rotation!);
      }
    } catch (e) {
      print('❌ Error creating InputImage: $e');
      return null;
    }
  }

  // Metode baru untuk menangani format yang tidak didukung langsung
  InputImage? _processUnsupportedFormat(
    CameraImage image,
    Size imageSize,
    InputImageRotation rotation,
  ) {
    try {
      print(
        "⚠️ Using fallback conversion for unsupported format: ${image.format.raw}",
      );

      // Untuk format apapun, coba gunakan plane pertama
      if (image.planes.isNotEmpty) {
        final plane = image.planes.first;

        // Force format sebagai NV21 yang paling umum didukung oleh ML Kit
        return InputImage.fromBytes(
          bytes: plane.bytes,
          metadata: InputImageMetadata(
            size: imageSize,
            rotation: rotation,
            format: InputImageFormat.nv21,
            bytesPerRow: plane.bytesPerRow,
          ),
        );
      }

      print("❌ No planes available in image");
      return null;
    } catch (e) {
      print("❌ Fallback format processing failed: $e");
      return null;
    }
  }

  // 3. TAMBAHKAN METHOD UNTUK CEK CAMERA CAPABILITIES
  Future<void> _checkCameraCapabilities() async {
    try {
      final cameras = await availableCameras();
      print("=== CAMERA CAPABILITIES ===");

      for (var camera in cameras) {
        print("Camera: ${camera.name}");
        print("  Direction: ${camera.lensDirection}");
        print("  Sensor Orientation: ${camera.sensorOrientation}");
      }

      if (_cameraController?.value.isInitialized == true) {
        print("=== CURRENT CAMERA CONFIG ===");
        print("Preview Size: ${_cameraController!.value.previewSize}");
        print("Aspect Ratio: ${_cameraController!.value.aspectRatio}");
        print("Is Streaming: ${_cameraController!.value.isStreamingImages}");

        // device orientation
        final deviceOrientation =
            _cameraController!.value.deviceOrientation.toString();
        print("Device Orientation: $deviceOrientation");
      }
      print("============================");
    } catch (e) {
      print("Error checking camera capabilities: $e");
    }
  }

  // 4. FALLBACK CAMERA INITIALIZATION
  Future<void> _initializeCameraWithFallback() async {
    // Ubah urutan preferensi format - YUV_420 di urutan pertama karena sepertinya itu yang digunakan
    final formatOptions = [
      ImageFormatGroup.yuv420, // Prioritaskan YUV_420 yang terdeteksi dalam log
      ImageFormatGroup.nv21,
      // ImageFormatGroup.jpeg - tidak untuk streaming
    ];

    final resolutionOptions = [
      ResolutionPreset.medium,
      ResolutionPreset.low,
      ResolutionPreset.high,
    ];

    for (var format in formatOptions) {
      for (var resolution in resolutionOptions) {
        try {
          print("🔄 Trying format: $format with resolution: $resolution");

          final cameras = await availableCameras();
          final frontCamera = cameras.firstWhere(
            (camera) => camera.lensDirection == CameraLensDirection.front,
          );

          final controller = CameraController(
            frontCamera,
            resolution,
            imageFormatGroup: format,
            enableAudio: false,
          );

          await controller.initialize();

          // Test dengan sample image
          bool formatWorking = await _testCameraFormat(controller);

          if (formatWorking) {
            print(
              "✅ SUCCESS: Format $format with resolution $resolution works!",
            );
            _cameraController = controller;
            this.frontCamera = frontCamera;
            return;
          } else {
            print("❌ Format $format with resolution $resolution failed test");
            await controller.dispose();
          }
        } catch (e) {
          print("❌ Failed format $format with resolution $resolution: $e");
        }
      }
    }

    throw Exception("No compatible camera format found!");
  }

  // 5. TEST METHOD UNTUK VALIDASI FORMAT
  Future<bool> _testCameraFormat(CameraController controller) async {
    try {
      bool testPassed = false;

      controller.startImageStream((CameraImage img) async {
        try {
          final inputImage = _getInputImageFromCameraImage(img);
          if (inputImage != null) {
            print("✅ InputImage created successfully");
            testPassed = true;
          }
        } catch (e) {
          print("❌ Test failed: $e");
        }

        // Stop stream after first test
        controller.stopImageStream();
      });

      // Wait for test
      await Future.delayed(Duration(milliseconds: 2000));

      return testPassed;
    } catch (e) {
      print("❌ Camera format test error: $e");
      return false;
    }
  }

  void _updateFaceInFrameStatus() {
    if (!_isFaceInFrame.value) {
      _isFaceInFrame.value = true;
      _addSuccessfulStep('Face in frame');

      if (status.value == LivenessStatus.detectingFace) {
        status.value = LivenessStatus.checkLeftRotation;
        currentInstruction.value = 'Great! Now rotate your face to the left';
        print("Face now in frame! Moving to LEFT rotation stage");
      }
    }
  }

  // Longgarkan batasan rotasi kepala ke kiri
  void _updateHeadRotationStatus(double? rotY) {
    // Longgarkan deteksi rotasi ke kiri, nilai -3 lebih kecil dari -5 sebelumnya
    if (_isFaceInFrame.value &&
        !_isFaceLeft.value &&
        rotY != null &&
        rotY < -3) {
      _isFaceLeft.value = true;
      _addSuccessfulStep('Face rotated left');

      if (status.value == LivenessStatus.checkLeftRotation) {
        status.value = LivenessStatus.checkRightRotation;
        currentInstruction.value = 'Good! Now rotate your face to the right';
        print("Left rotation detected! Moving to RIGHT rotation stage");
      }
    }

    // Longgarkan deteksi rotasi ke kanan, nilai 3 lebih kecil dari 5 sebelumnya
    if (_isFaceLeft.value && !_isFaceRight.value && rotY != null && rotY > 3) {
      _isFaceRight.value = true;
      _addSuccessfulStep('Face rotated right');

      if (status.value == LivenessStatus.checkRightRotation) {
        status.value = LivenessStatus.checkSmile;
        currentInstruction.value = 'Great! Now smile for the camera';
        print("Right rotation detected! Moving to SMILE stage");
      }
    }
  }

  // Longgarkan kondisi senyum
  void _updateSmilingStatus(double smileProb) {
    if (_isFaceInFrame.value &&
        _isFaceLeft.value &&
        _isFaceRight.value &&
        !_isSmiled.value &&
        smileProb > 0.1) {
      // Nilai diturunkan dari 0.2 menjadi 0.1
      _isSmiled.value = true;
      _addSuccessfulStep('Smiling');

      if (status.value == LivenessStatus.checkSmile) {
        status.value = LivenessStatus.checkEyesOpen;
        currentInstruction.value = 'Excellent! Now open your eyes wide';
        print("Smile detected! Moving to EYES OPEN stage");
      }
    }
  }

  // Longgarkan kondisi mata terbuka
  void _updateEyeOpenStatus(double leftEyeOpen, double rightEyeOpen) {
    if (_isFaceInFrame.value &&
        _isFaceLeft.value &&
        _isFaceRight.value &&
        _isSmiled.value &&
        !_isEyeOpen.value) {
      if (leftEyeOpen > 0.1 && rightEyeOpen > 0.1) {
        // Nilai diturunkan dari 0.2 menjadi 0.1
        _isEyeOpen.value = true;
        _addSuccessfulStep('Eyes Open');

        if (status.value == LivenessStatus.checkEyesOpen) {
          status.value = LivenessStatus.readyForPhoto;
          currentInstruction.value = 'Perfect! Hold still for photo capture';
          print("Eyes open detected! Moving to READY FOR PHOTO stage");
        }
      }
    }
  }

  // Longgarkan kondisi untuk siap foto
  void _updateFaceInFrameForPhotoStatus(double? rotY, double? smileProb) {
    // Longgarkan kondisi rotasi dan senyum
    if (_isFaceRight.value &&
        _isFaceLeft.value &&
        rotY != null &&
        rotY > -5 && // Nilai lebih longgar dari -3
        rotY < 5 && // Nilai lebih longgar dari 3
        smileProb != null) // Hapus batasan senyum
    {
      _isFaceReadyForPhoto.value = true;
      _addSuccessfulStep('Face Ready For Photo');

      if (status.value == LivenessStatus.checkEyesOpen) {
        status.value = LivenessStatus.readyForPhoto;
        currentInstruction.value = 'Perfect! Hold still for photo capture';
        print("Face ready for photo! Moving to READY FOR PHOTO stage");
      }
    } else {
      _isFaceReadyForPhoto.value = false;
    }
  }

  // Ganti metode _isFaceInsideFrame untuk selalu mengembalikan true
  bool _isFaceInsideFrame(Rect boundingBox) {
    // Selalu kembalikan true tanpa memeriksa batas-batas
    return true;
  }

  void _addSuccessfulStep(String step) {
    if (!_successfulSteps.contains(step)) {
      _successfulSteps.add(step);
    }
  }

  // Tambahkan metode untuk memangkas frame dan otomatis menyelesaikan proses verifikasi
  void autoCompleteVerification() {
    print("Auto-completing verification process");

    // Set semua status
    _isFaceInFrame.value = true;
    _addSuccessfulStep('Face in frame');

    _isFaceLeft.value = true;
    _addSuccessfulStep('Face rotated left');

    _isFaceRight.value = true;
    _addSuccessfulStep('Face rotated right');

    _isSmiled.value = true;
    _addSuccessfulStep('Smiling');

    _isEyeOpen.value = true;
    _addSuccessfulStep('Eyes Open');

    _isFaceReadyForPhoto.value = true;
    _addSuccessfulStep('Face Ready For Photo');

    // Tangkap gambar
    _captureImage();
  }

  // Tambahkan metode ini agar di debug panel bisa dipanggil
  void skipAllVerificationSteps() {
    autoCompleteVerification();
  }

  // Modifikasi metode _processCameraImage untuk lebih relaks dalam deteksi wajah
  Future<void> _processCameraImage(CameraImage img) async {
    // Avoid processing if already captured or currently processing
    if (_isCaptured.value || _processingImage) return;

    _processingImage = true;

    try {
      // Tambah delay untuk memberikan waktu lebih untuk pemrosesan
      await Future.delayed(Duration(milliseconds: 50));

      // Use microtask untuk memberikan waktu lebih pada face detection
      await Future.microtask(() async {
        final inputImage = _getInputImageFromCameraImage(img);
        if (inputImage == null) {
          _processingImage = false;
          return;
        }

        // Log ukuran gambar untuk membantu debug zoom/resolusi
        // print("Processing image of size: ${img.width}x${img.height}");

        // Berikan timeout yang lebih panjang untuk deteksi wajah (5 detik)
        List<Face> faces = [];
        try {
          // Gunakan timeout untuk mencegah pemrosesan yang terlalu lama
          faces = await _faceDetector
              .processImage(inputImage)
              .timeout(
                Duration(seconds: 3),
                onTimeout: () {
                  print("⚠️ Face detection timed out after 3 seconds");
                  return [];
                },
              );

          print("Detected ${faces.length} faces");
        } catch (e) {
          print("Face detection error: $e");
        }

        // Process face detection results
        if (faces.isNotEmpty) {
          await _processFaces(faces);
        } else {
          _handleNoFacesDetected();
        }
      });
    } catch (e) {
      print('Error in image processing: $e');
    } finally {
      // Ensure _processingImage is reset even if an error occurs
      _processingImage = false;
    }
  }

  // New method to process detected faces
  Future<void> _processFaces(List<Face> faces) async {
    // Pilih wajah terbaik berdasarkan ukuran (prioritaskan wajah yang lebih besar)
    Face? bestFace;
    double largestArea = 0;

    for (var face in faces) {
      final area = face.boundingBox.width * face.boundingBox.height;
      if (area > largestArea) {
        largestArea = area;
        bestFace = face;
      }
    }

    // Periksa apakah wajah terbaik memenuhi ukuran minimum yang direkomendasikan
    if (bestFace != null) {
      final faceWidth = bestFace.boundingBox.width;
      final faceHeight = bestFace.boundingBox.height;

      print("Best face size: ${faceWidth}x$faceHeight");

      // Pastikan wajah cukup besar untuk deteksi (minimal 80x80 pixel)
      // Reduced from 100x100 to improve detection with varied camera resolutions
      if (faceWidth >= 80 && faceHeight >= 80) {
        _isNoFace.value = false;
        _isMultiFace.value = false;
        await _compareFaces(bestFace);

        if (!_isDifferentPerson.value) {
          _handleFaceDetection(bestFace);
        } else {
          _duplicatePersonFaceDetect();
        }
      } else {
        print(
          "Face too small: ${faceWidth}x$faceHeight, minimum 80x80 required",
        );
        _isNoFace.value = true;
        currentInstruction.value = 'Please move closer to the camera';
      }
    } else if (faces.isNotEmpty) {
      // If we have faces but none meet our "best face" criteria, use the first one anyway
      _isNoFace.value = false;
      _isMultiFace.value = faces.length > 1;
      final face = faces.first;
      await _compareFaces(face);
      _handleFaceDetection(face);
    }
  }

  // New method to handle when no faces are detected
  void _handleNoFacesDetected() {
    _isNoFace.value = true;

    // Don't reset progress if already started
    if (!_isFaceInFrame.value) {
      status.value = LivenessStatus.detectingFace;
      currentInstruction.value =
          'No face detected. Please position your face in the frame and ensure good lighting.';
    }
  }

  // Tambahkan variabel untuk mencegah pemrosesan berlebihan
  bool _processingImage = false;

  // Tambahkan metode retryDetection untuk mencoba ulang jika terjadi kegagalan deteksi
  void retryDetection() {
    if (_cameraController != null && _cameraController!.value.isInitialized) {
      // Reset flag proses tapi pertahankan kemajuan
      _processingImage = false;

      // Update pesan instruksi
      currentInstruction.value = 'Retrying face detection...';

      // Tampilkan debug info
      print('Retrying face detection...');
    }
  }

  // // Improve camera input image processing
  // InputImage? _getInputImageFromCameraImage(CameraImage image) {
  //   final sensorOrientation = frontCamera.sensorOrientation;
  //   InputImageRotation? rotation;

  //   if (Platform.isIOS) {
  //     rotation = InputImageRotationValue.fromRawValue(sensorOrientation);
  //   } else if (Platform.isAndroid) {
  //     var rotationCompensation =
  //         orientations[_cameraController!.value.deviceOrientation];
  //     if (rotationCompensation == null) {
  //       print("Warning: null rotation compensation");
  //       rotationCompensation = 0; // Provide default value
  //     }

  //     if (frontCamera.lensDirection == CameraLensDirection.front) {
  //       rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
  //     } else {
  //       rotationCompensation =
  //           (sensorOrientation - rotationCompensation + 360) % 360;
  //     }
  //     rotation = InputImageRotationValue.fromRawValue(rotationCompensation!);
  //   }

  //   if (rotation == null) {
  //     print("Warning: null input image rotation");
  //     return null;
  //   }

  //   final format = InputImageFormatValue.fromRawValue(image.format.raw);
  //   if (format == null) {
  //     print(
  //       "Warning: null input image format from raw value: ${image.format.raw}",
  //     );
  //     return null;
  //   }

  //   if ((Platform.isAndroid && format != InputImageFormat.nv21) ||
  //       (Platform.isIOS && format != InputImageFormat.bgra8888)) {
  //     print("Warning: unexpected format for platform: $format");
  //     return null;
  //   }

  //   if (image.planes.length != 1) {
  //     print("Warning: expected 1 plane, got ${image.planes.length}");
  //     return null;
  //   }

  //   final plane = image.planes.first;

  //   return InputImage.fromBytes(
  //     bytes: plane.bytes,
  //     metadata: InputImageMetadata(
  //       size: Size(image.width.toDouble(), image.height.toDouble()),
  //       rotation: rotation,
  //       format: format,
  //       bytesPerRow: plane.bytesPerRow,
  //     ),
  //   );
  // }

  // Metode untuk ambil foto dengan ukuran penuh
  Future<void> _captureImage() async {
    if (_cameraController!.value.isTakingPicture) return;

    try {
      status.value = LivenessStatus.photoTaken;
      currentInstruction.value = 'Capturing photo...';

      // Hentikan stream untuk foto berkualitas tinggi
      await _cameraController!.stopImageStream();

      // Beri jeda singkat agar kamera stabil
      await Future.delayed(Duration(milliseconds: 500));

      // Ambil foto dengan resolusi penuh
      final XFile file = await _cameraController!.takePicture();
      print("Image captured: ${file.path}");

      // Set status captured
      _isCaptured.value = true;
      _capturedImage.value = file;

      // Verifikasi bahwa gambar memenuhi kriteria minimum (min 480x360)
      final imageFile = File(file.path);
      if (await imageFile.exists()) {
        final fileSize = await imageFile.length();
        print("Captured image size: $fileSize bytes");

        if (fileSize < 5000) {
          // Gambar terlalu kecil mungkin rusak
          print("Warning: Captured image is too small ($fileSize bytes)");
          // Bisa retry atau handle error di sini
        }
      }

      status.value = LivenessStatus.completed;
      currentInstruction.value = 'Liveness check successful!';

      // Tutup detektor wajah untuk hemat resources
      _faceDetector.close();
    } catch (e) {
      print('Error capturing image: $e');
      status.value = LivenessStatus.failed;
      currentInstruction.value = 'Failed to capture image: $e';

      // Restart preview jika gagal
      try {
        _cameraController!.startImageStream((CameraImage img) {
          _processCameraImage(img);
        });
      } catch (e) {
        print('Error restarting preview: $e');
      }
    }
  }

  // Handle detection of a different person (duplicate face)
  void _duplicatePersonFaceDetect() {
    print(
      "Different person detected! Please ensure only one person is in front of the camera.",
    );
    _isDifferentPerson.value = true;
    currentInstruction.value =
        'Different person detected. Please ensure only one person is in front of the camera.';
    status.value = LivenessStatus.failed;
  }

  // Face comparison methods
  Future<List<double>> _extractFaceEmbeddings(Face face) async {
    return [
      face.boundingBox.left,
      face.boundingBox.top,
      face.boundingBox.right,
      face.boundingBox.bottom,
    ];
  }

  Future<void> _compareFaces(Face currentFace) async {
    final currentEmbedding = await _extractFaceEmbeddings(currentFace);

    if (_firstPersonEmbedding == null) {
      _firstPersonEmbedding = currentEmbedding;
    } else {
      final double similarity = _calculateSimilarity(
        _firstPersonEmbedding!,
        currentEmbedding,
      );
      _isDifferentPerson.value = similarity < 0.8;
    }
  }

  double _calculateSimilarity(
    List<double> embedding1,
    List<double> embedding2,
  ) {
    double dotProduct = 0.0;
    double norm1 = 0.0;
    double norm2 = 0.0;

    for (int i = 0; i < embedding1.length; i++) {
      dotProduct += embedding1[i] * embedding2[i];
      norm1 += embedding1[i] * embedding1[i];
      norm2 += embedding2[i] * embedding2[i];
    }

    return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
  }

  String getCurrentDirection() {
    // Use the currentInstruction instead
    return currentInstruction.value;
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    final CameraController? cameraController = _cameraController;
    if (cameraController == null || !cameraController.value.isInitialized) {
      return;
    }
    if (state == AppLifecycleState.inactive) {
      cameraController.dispose();
    } else if (state == AppLifecycleState.resumed) {
      _initializeCamera();
    }
  }

  // Track if user left the screen
  final RxBool wasDetectionCancelled = RxBool(false);

  // Add a method to handle cleanup when users cancel
  void handleCancellation() {
    wasDetectionCancelled.value = true;

    // Make sure to update the selfie controller
    if (Get.isRegistered<SelfieVerificationController>()) {
      final selfieController = Get.find<SelfieVerificationController>();
      selfieController.cancelLivenessDetection();
    }
  }

  @override
  void onClose() {
    // If detection was not complete and not cancelled already, mark as cancelled
    if (!isCaptured && !wasDetectionCancelled.value) {
      handleCancellation();
    }

    _faceDetector.close();
    if (_cameraController != null) _cameraController!.dispose();
    WidgetsBinding.instance.removeObserver(this);
    _faceMeshDetector.close();
    super.onClose();
  }

  /// Generate a FaceModel from the captured image
  FaceModel generateFaceModel() {
    if (_capturedImage.value == null) {
      return FaceModel.empty();
    }

    final uuid = Uuid();

    return FaceModel(
      imagePath: _capturedImage.value!.path,
      faceId: uuid.v4(),
      confidence: 0.95,
      boundingBox: {'x': 0.1, 'y': 0.1, 'width': 0.8, 'height': 0.8},
    ).withLiveness(
      isLive: true,
      confidence: 0.92,
      message: 'Liveness check passed successfully',
    );
  }

  // Add a helper method to manually advance for testing purposes
  void forceAdvanceToNextStep() {
    switch (status.value) {
      case LivenessStatus.detectingFace:
        _isFaceInFrame.value = true;
        _addSuccessfulStep('Face in frame');
        status.value = LivenessStatus.checkLeftRotation;
        currentInstruction.value = 'Great! Now rotate your face to the left';
        print("Forced: Face in frame step completed");
        break;
      case LivenessStatus.checkLeftRotation:
        _isFaceLeft.value = true;
        _addSuccessfulStep('Face rotated left');
        status.value = LivenessStatus.checkRightRotation;
        currentInstruction.value = 'Good! Now rotate your face to the right';
        print("Forced: Face left rotation step completed");
        break;
      case LivenessStatus.checkRightRotation:
        _isFaceRight.value = true;
        _addSuccessfulStep('Face rotated right');
        status.value = LivenessStatus.checkSmile;
        currentInstruction.value = 'Great! Now smile for the camera';
        print("Forced: Face right rotation step completed");
        break;
      case LivenessStatus.checkSmile:
        _isSmiled.value = true;
        _addSuccessfulStep('Smiling');
        status.value = LivenessStatus.checkEyesOpen;
        currentInstruction.value = 'Excellent! Now open your eyes wide';
        print("Forced: Smile step completed");
        break;
      case LivenessStatus.checkEyesOpen:
        _isEyeOpen.value = true;
        _isFaceReadyForPhoto.value = true;
        _addSuccessfulStep('Eyes Open');
        _addSuccessfulStep('Face Ready For Photo');
        status.value = LivenessStatus.readyForPhoto;
        currentInstruction.value = 'Perfect! Hold still for photo capture';
        print("Forced: Eyes open step completed");
        break;
      case LivenessStatus.readyForPhoto:
        forceCaptureImage();
        break;
      default:
        print("Forced: No action for current state: ${status.value}");
        break;
    }
  }

  // Method to force capture image manually (for debugging)
  Future<void> forceCaptureImage() async {
    if (_cameraController == null || !_cameraController!.value.isInitialized) {
      print("Cannot force capture: camera not initialized");
      return;
    }

    if (_cameraController!.value.isTakingPicture) {
      print("Cannot force capture: camera already taking picture");
      return;
    }

    try {
      print("Forcing manual image capture...");
      status.value = LivenessStatus.photoTaken;
      currentInstruction.value = 'Capturing photo...';

      final XFile file = await _cameraController!.takePicture();
      _isCaptured.value = true;
      _capturedImage.value = file;

      status.value = LivenessStatus.completed;
      currentInstruction.value = 'Liveness check successful! (Manual capture)';
      print("Manual image capture successful");
    } catch (e) {
      print('Error during manual image capture: $e');
      status.value = LivenessStatus.failed;
      currentInstruction.value = 'Failed to capture image manually: $e';
    }
  }

  //  Reset Process
  void resetProcess() {
    // Reset all state variables
    _isFaceInFrame.value = false;
    _isFaceLeft.value = false;
    _isFaceRight.value = false;
    _isEyeOpen.value = false;
    _isNoFace.value = false;
    _isMultiFace.value = false;
    _isCaptured.value = false;
    _isSmiled.value = false;
    _isFaceReadyForPhoto.value = false;
    _isDifferentPerson.value = false;
    _processingImage = false;
    _throttled = false;

    status.value = LivenessStatus.preparing;
    currentInstruction.value = 'Initializing camera...';
    _successfulSteps.clear();

    // Try to adjust camera parameters if controller exists
    if (_cameraController != null && _cameraController!.value.isInitialized) {
      // Reset zoom to minimum for better face detection
      _cameraController!
          .getMinZoomLevel()
          .then((minZoom) {
            _cameraController!.setZoomLevel(minZoom);
          })
          .catchError((e) {
            print("Failed to reset zoom level: $e");
          });
    } else {
      // Reinitialize camera if needed
      _initializeCamera();
    }
  }

  // Kunci orientasi ke portrait
  Future<void> _lockDeviceOrientation() async {
    try {
      print("🔒 Locking device orientation to portrait");
      await SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
      ]);
      print("✅ Device orientation locked to portrait");
    } catch (e) {
      print("❌ Failed to lock orientation: $e");
    }
  }

  // Tambahkan fungsi untuk mengembalikan orientasi
  Future<void> _resetDeviceOrientation() async {
    try {
      print("🔓 Resetting device orientation");
      await SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
        DeviceOrientation.portraitDown,
        DeviceOrientation.landscapeLeft,
        DeviceOrientation.landscapeRight,
      ]);
    } catch (e) {
      print("❌ Failed to reset orientation: $e");
    }
  }

  String _getCurrentDeviceOrientation() {
    final orientation = MediaQuery.of(Get.context!).orientation;
    return orientation == Orientation.portrait ? "Portrait" : "Landscape";
  }
}


import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

class CameraScreen extends StatefulWidget {
  const CameraScreen({super.key});
  
  @override
  State<CameraScreen> createState() => _CameraScreenState();
}

class _CameraScreenState extends State<CameraScreen>
    with WidgetsBindingObserver, TickerProviderStateMixin {
  CameraController? controller;
  String error='';
  
  FaceDetector faceDetector = FaceDetector(
    options: FaceDetectorOptions(
      enableClassification: true,
      enableContours: true,
      enableTracking: true,
      enableLandmarks: true,
      performanceMode: Platform.isAndroid
          ? FaceDetectorMode.fast
          : FaceDetectorMode.accurate,
    ),
  );

  final orientations = {
    DeviceOrientation.portraitUp: 0,
    DeviceOrientation.landscapeLeft: 90,
    DeviceOrientation.portraitDown: 180,
    DeviceOrientation.landscapeRight: 270,
  };
  
  initialize() async {
    controller = CameraController(
      cameras[1],
      ResolutionPreset.medium,
      imageFormatGroup: Platform.isAndroid
          ? ImageFormatGroup.nv21
          : ImageFormatGroup.bgra8888,
    );
    if (mounted) setState(() {});
    initializeCameraController(controller!.description);
    if (mounted) setState(() {});
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    initialize();
  }

  @override
  void dispose() {
    faceDetector.close();
    if (controller != null) controller!.dispose();
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    final CameraController? cameraController = controller;
    if (cameraController == null || !cameraController.value.isInitialized) {
      return;
    }

    if (state == AppLifecycleState.inactive) {
      cameraController.dispose();
    } else if (state == AppLifecycleState.resumed) {
     initialize();
    }
  }

  Future<void> initializeCameraController() async {
    try {
      await controller!.initialize();
      controller!.setFlashMode(FlashMode.off);
      // If the controller is updated then update the UI.
      controller!.addListener(() {
        if (mounted) setState(() {});
         controller!.startImageStream(_processCameraImage).then((value) {});
      });
      if (mounted) setState(() {});
    } on CameraException catch (e) {
      switch (e.code) {
        case 'CameraAccessDenied':
          Get.back();
          cameraDialog();
          break;
        case 'CameraAccessDeniedWithoutPrompt':
          Get.back();
          cameraDialog();
          break;
        case 'CameraAccessRestricted':
          Get.back();
          cameraDialog();
          break;
        default:
          Get.back();
          EasyLoading.showError(e.description!);
          break;
      }
    }
    if (mounted) setState(() {});
  }

  cameraDialog() {
    Get.defaultDialog(
      title: 'Camera Access',
      middleText:
          'Looks like you have not provided permission to camera completely. Enable it in the settings.',
      onConfirm: () async {
        Get.back();
        await openAppSettings();
      },
    );
  }

  clearCaptured(String message) {
    debugPrint(message);
    imageFile = null;
    error = message;
    if (mounted) setState(() {});
    return;
  }

  void _processCameraImage(CameraImage image) {
    final inputImage = inputImageFromCameraImage(image);
    if (inputImage == null) return;
    checkImage(inputImage);
  }

  checkImage(InputImage inputImage) async {
    List<Face> faces = await faceDetector.processImage(inputImage);
    debugPrint('faces: $faces');
    if (faces.isEmpty) {
      clearCaptured('No faces detected in the image.');
    }
    if (faces.length > 1) {
      clearCaptured(
        'Multiple faces detected. Please try capturing a single face.',
      );
    }
    // Handle the detected faces
    for (Face face in faces) {
      if (face.leftEyeOpenProbability != null &&
          face.rightEyeOpenProbability != null) {
        final leftEyeOpen = face.leftEyeOpenProbability! > 0.5;
        final rightEyeOpen = face.rightEyeOpenProbability! > 0.5;
        if (leftEyeOpen && rightEyeOpen) {
          error = 'Click capture to save this image';
          if (mounted) setState(() {});
          return;
        } else if (!leftEyeOpen && !rightEyeOpen) {
          clearCaptured('Both eyes are closed!');
        } else if (leftEyeOpen && !rightEyeOpen) {
          clearCaptured('Left eye is open, right eye is closed.');
        } else if (!leftEyeOpen && rightEyeOpen) {
          clearCaptured('Right eye is open, left eye is closed.');
        }
      } else {
        clearCaptured(
          'Looks like either one or both eyes have not been captured properly. Please try again.',
        );
      }
    }
  }


  InputImage? inputImageFromCameraImage(CameraImage image) {
    final camera = cameras[1];
    final sensorOrientation = camera.sensorOrientation;
    InputImageRotation? rotation;
    if (Platform.isIOS) {
      rotation = InputImageRotationValue.fromRawValue(sensorOrientation);
    } else if (Platform.isAndroid) {
      var rotationCompensation =
          orientations[controller!.value.deviceOrientation];
      if (rotationCompensation == null) return null;
      if (camera.lensDirection == CameraLensDirection.front) {
        // front-facing
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
      } else {
        // back-facing
        rotationCompensation =
            (sensorOrientation - rotationCompensation + 360) % 360;
      }
      rotation = InputImageRotationValue.fromRawValue(rotationCompensation);
    }
    if (rotation == null) return null;

    // get image format
    final format = InputImageFormatValue.fromRawValue(image.format.raw);

    if (format == null ||
        (Platform.isAndroid && format != InputImageFormat.nv21) ||
        (Platform.isIOS && format != InputImageFormat.bgra8888)) return null;

    // since format is constraint to nv21 or bgra8888, both only have one plane
    if (image.planes.length != 1) return null;
    final plane = image.planes.first;

    // compose InputImage using bytes
    return InputImage.fromBytes(
      bytes: plane.bytes,
      metadata: InputImageMetadata(
        size: Size(image.width.toDouble(), image.height.toDouble()),
        rotation: rotation, // used only in Android
        format: format, // used only in iOS
        bytesPerRow: plane.bytesPerRow, // used only in iOS
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text('Capture Your Selfie'),
      ),
      bottomNavigationBar: controller == null
          ? null
          : Column(
              mainAxisSize: MainAxisSize.min,
              children: [
               Text(error), 
               SizedBox(height:30), 
                ElevatedButton(
                  child: Text('Capture'),
                  onPressed: () async {
                   await controller!.takePicture().then((value) {});
                  },
                ),        
               SizedBox(height:20),
              ],
            ),
      body: controller == null
          ? Center(child: Text('Need to access camera to capture selfie'))
          : CameraPreview(controller!),
    );
  }
}