package com.ar.imagetracear.fragment;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
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.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LiveData;
import androidx.recyclerview.widget.GridLayoutManager;


import com.ar.imagetracear.databinding.FragmentImportBinding;
import com.ar.imagetracear.database.AppDatabase;
import com.ar.imagetracear.database.ImageTraceEntry;
import com.ar.imagetracear.database.ImageTraceEntryDao;
import com.ar.imagetracear.adapter.ImportAdapter;
import com.ar.imagetracear.utils.ItemDecoration;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class ImportFragment extends Fragment implements ImportAdapter.OnHeaderClickListener {
    private FragmentImportBinding binding;
    private ImportAdapter adapter;
    private ImageTraceEntryDao dbDao;
    private final List<String> pathCache = new ArrayList<>();
    private static final int IMAGE_SIZE_LIMIT = 5 * 1024 * 1024;

    private final ActivityResultLauncher<Intent> pickerLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                    Uri selected = result.getData().getData();
                    if (selected != null) {
                        handlePickedImage(selected);
                    }
                }
            });

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        binding = FragmentImportBinding.inflate(inflater, container, false);

        initializeDatabase();
        prepareImageGrid();
        subscribeToImageUpdates();

        return binding.getRoot();
    }

    private void initializeDatabase() {
        dbDao = AppDatabase.getInstance(requireContext()).imageTraceEntryDao();
    }

    private void prepareImageGrid() {
        binding.recyclerview.setLayoutManager(new GridLayoutManager(requireContext(), 2));
        binding.recyclerview.addItemDecoration(new ItemDecoration(16, 19, 10));
        adapter = new ImportAdapter(new ArrayList<>(), requireActivity(), this);
        binding.recyclerview.setAdapter(adapter);
    }

    private void subscribeToImageUpdates() {
        LiveData<List<ImageTraceEntry>> liveImages = dbDao.getImportLive();
        liveImages.observe(getViewLifecycleOwner(), updated -> adapter.updateList(updated));
    }

    @Override
    public void onResume() {
        super.onResume();
        adapter.notifyDataSetChanged();
    }

    @Override
    public void onHeaderClick() {
        openImageChooser();
    }

    private void openImageChooser() {
        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        pickerLauncher.launch(intent);
    }

    private void handlePickedImage(Uri uri) {
        try {
            if (checkImageSize(uri)) {
                persistImage(uri);
            } else {
                Toast.makeText(requireContext(), "Image too large", Toast.LENGTH_SHORT).show();
            }
        } catch (IOException ex) {
            Toast.makeText(requireContext(), "Error reading image: " + ex.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private boolean checkImageSize(Uri uri) throws IOException {
        try (InputStream input = requireContext().getContentResolver().openInputStream(uri)) {
            return input != null && input.available() <= IMAGE_SIZE_LIMIT;
        }
    }

    private void persistImage(Uri uri) {
        try {
            InputStream input = requireContext().getContentResolver().openInputStream(uri);
            Bitmap bitmap = BitmapFactory.decodeStream(input);
            if (bitmap == null) {
                Toast.makeText(requireContext(), "Cannot decode image", Toast.LENGTH_SHORT).show();
                return;
            }

            File targetFile = new File(requireContext().getFilesDir(), System.currentTimeMillis() + ".jpg");
            try (FileOutputStream out = new FileOutputStream(targetFile)) {
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
            }
            if (input != null) input.close();

            saveToDb(targetFile.getAbsolutePath(), targetFile);

        } catch (IOException e) {
            Toast.makeText(requireContext(), "Failed to save image: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private void saveToDb(String imagePath, File file) {
        new Thread(() -> {
            if (imageAlreadyExists(imagePath, file)) {
                requireActivity().runOnUiThread(() ->
                        Toast.makeText(requireContext(), "Duplicate image", Toast.LENGTH_SHORT).show());
                file.delete();
                return;
            }
            pathCache.add(imagePath);
            dbDao.insert(new ImageTraceEntry(false, true, imagePath));
        }).start();
    }

    private boolean imageAlreadyExists(String imagePath, File fileToCheck) {
        for (String existing : pathCache) {
            if (sameFileSize(new File(existing), fileToCheck)) return true;
        }
        List<ImageTraceEntry> stored = dbDao.getImport();
        for (ImageTraceEntry item : stored) {
            if (sameFileSize(new File(item.getPath()), fileToCheck)) return true;
        }
        return false;
    }

    private boolean sameFileSize(File a, File b) {
        return a.length() == b.length();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
}