package com.example.epl498_labs.ui.sensors;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LifecycleOwner;

import com.example.epl498_labs.R;
import com.google.common.util.concurrent.ListenableFuture;

import android.content.Intent;
import android.net.Uri;

import com.google.mlkit.vision.barcode.BarcodeScanning;
import com.google.mlkit.vision.barcode.common.Barcode;
import com.google.mlkit.vision.barcode.BarcodeScanner;
import com.google.mlkit.vision.common.InputImage;
import java.util.concurrent.ExecutionException;

public class CameraQRFragment extends Fragment {

    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
    private PreviewView previewView;
    private Camera camera;
    private BarcodeScanner barcodeScanner;

    // Define the permissions you want to request
    private static final String[] REQUIRED_PERMISSIONS = {
            Manifest.permission.CAMERA,
    };

    // Request code for permission launcher
    private final ActivityResultLauncher<String[]> requestPermissionLauncher =
            registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), permissions -> {
                boolean allPermissionsGranted = true;
                for (Boolean permission : permissions.values()) {
                    if (!permission) {
                        allPermissionsGranted = false;
                        break;
                    }
                }

                if (allPermissionsGranted) {
                    // All permissions are granted, you can start the camera
                    startCamera();
                } else {
                    // Permission(s) denied, handle accordingly (e.g., show a message)
                    Toast.makeText(requireContext(), "Permissions not granted.", Toast.LENGTH_SHORT).show();
                    requireActivity().finish();
                }
            });

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment (UI defined in fragment_camera_qr.xml)
        View view = inflater.inflate(R.layout.fragment_camera_qr, container, false);

        // Get a reference to the PreviewView in the layout (where the camera feed will be displayed)
        previewView = view.findViewById(R.id.preview_view);

        // Initialize the ML Kit BarcodeScanner (used later in processImageProxy for QR detection)
        barcodeScanner = BarcodeScanning.getClient();
        // Request camera permissions
        requestCameraPermission();

        return view;
    }

    // Request camera permissions based on the REQUIRED_PERMISSIONS array
    private void requestCameraPermission() {
        if (allPermissionsGranted()) {
            startCamera();
        } else {
            requestPermissionLauncher.launch(REQUIRED_PERMISSIONS);
        }
    }

    // Initialize and start the camera
    private void startCamera() {
        cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext());
        cameraProviderFuture.addListener(() -> {
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                bindCameraUseCases(cameraProvider);
            } catch (ExecutionException | InterruptedException e) {
                Log.e("CameraQRFragment", "Error initializing cameraProviderFuture: " + e.getMessage());
            }
        }, ContextCompat.getMainExecutor(requireContext()));
    }

    // Bind camera use cases such as preview and image capture
    private void bindCameraUseCases(ProcessCameraProvider cameraProvider) {
        // Selects which camera to use (front or back)
        // LENS_FACING_BACK (rear camera) or LENS_FACING_FRONT (selfie camera)
        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();

        // To be able to view what the camera "sees"
        Preview preview = new Preview.Builder().build();
        preview.setSurfaceProvider(previewView.getSurfaceProvider());

        // Streams live frames to our analyzer (ML Kit barcode scanner)
        ImageAnalysis imageAnalysis =
                new ImageAnalysis.Builder()
                        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                        .build();

        imageAnalysis.setAnalyzer(
                ContextCompat.getMainExecutor(requireContext()),
                imageProxy -> processImageProxy(imageProxy)
        );

        // Unbind previous use cases (if any)
        if (camera != null) {
            cameraProvider.unbindAll();
        }

        // Bind the camera to the lifecycle and store it in the camera member variable
        camera = cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, preview, imageAnalysis);
    }

    // QR code processing with ML Kit
    private void processImageProxy(ImageProxy imageProxy) {
        @SuppressWarnings("UnsafeOptInUsageError")
        InputImage image =
                InputImage.fromMediaImage(imageProxy.getImage(), imageProxy.getImageInfo().getRotationDegrees());

        barcodeScanner.process(image)
                .addOnSuccessListener(barcodes -> {
                    for (Barcode barcode : barcodes) {
                        if (barcode.getFormat() == Barcode.FORMAT_QR_CODE) {
                            String rawValue = barcode.getRawValue();
                            Log.d("CameraFragment", "QR Code detected: " + rawValue);

                            // Here we take action, in this case we want to navigate to the link
                            if (rawValue != null && (rawValue.startsWith("http://") || rawValue.startsWith("https://"))) {
                                // So, option 1: Open immediately in browser
                                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(rawValue));
                                startActivity(browserIntent);

                            } else {
                                // Not a URL, just show the text
                                Toast.makeText(requireContext(), "QR Code: " + rawValue, Toast.LENGTH_SHORT).show();
                            }
                        }
                    }
                })
                .addOnFailureListener(e -> Log.e("CameraFragment", "QR scan failed", e))
                .addOnCompleteListener(task -> imageProxy.close()); // we need to always close the listener!
    }

    // Check if all required permissions are granted
    private boolean allPermissionsGranted() {
        for (String permission : REQUIRED_PERMISSIONS) {
            if (ContextCompat.checkSelfPermission(requireContext(), permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }


    @Override
    public void onResume() {
        super.onResume();
        // Reinitialize the camera if it was released
        if (camera == null) {
            startCamera();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        releaseCamera(); // Release the camera resources when the fragment is destroyed
    }

    // Release camera resources
    private void releaseCamera() {
        if (cameraProviderFuture != null) {
            cameraProviderFuture.addListener(() -> {
                try {
                    ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                    cameraProvider.unbindAll(); // Release the camera
                } catch (ExecutionException | InterruptedException e) {
                    Log.e("CameraQRFragment", "Error releasing camera: " + e.getMessage());
                }
            }, ContextCompat.getMainExecutor(requireContext()));
        }
    }
}
