package com.atridad.ascently.utils

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageDecoder
import android.net.Uri
import androidx.core.graphics.scale
import androidx.exifinterface.media.ExifInterface
import java.io.File
import java.io.FileOutputStream
import java.util.UUID

object ImageUtils {

    private const val IMAGES_DIR = "problem_images"
    private const val MAX_IMAGE_SIZE = 1024
    private const val IMAGE_QUALITY = 85

    // Creates the images directory if it doesn't exist
    fun getImagesDirectory(context: Context): File {
        val imagesDir = File(context.filesDir, IMAGES_DIR)
        if (!imagesDir.exists()) {
            imagesDir.mkdirs()
        }
        return imagesDir
    }

    /** Saves an image from a URI while preserving EXIF orientation data */
    private fun saveImageWithExif(
        context: Context,
        imageUri: Uri,
        originalBitmap: Bitmap,
        outputFile: File,
    ): Boolean {
        return try {
            // Get EXIF data from original image
            val originalExif =
                context.contentResolver.openInputStream(imageUri)?.use { input ->
                    ExifInterface(input)
                }

            // Compress and save the bitmap
            val compressedBitmap = compressImage(originalBitmap)
            FileOutputStream(outputFile).use { output ->
                compressedBitmap.compress(Bitmap.CompressFormat.JPEG, IMAGE_QUALITY, output)
            }

            // Copy EXIF data to the saved file
            originalExif?.let { sourceExif ->
                val destExif = ExifInterface(outputFile.absolutePath)

                // Copy orientation and other important EXIF attributes
                val orientationValue = sourceExif.getAttribute(ExifInterface.TAG_ORIENTATION)
                orientationValue?.let { destExif.setAttribute(ExifInterface.TAG_ORIENTATION, it) }

                // Copy other useful EXIF data
                sourceExif.getAttribute(ExifInterface.TAG_DATETIME)?.let {
                    destExif.setAttribute(ExifInterface.TAG_DATETIME, it)
                }
                sourceExif.getAttribute(ExifInterface.TAG_GPS_LATITUDE)?.let {
                    destExif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, it)
                }
                sourceExif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE)?.let {
                    destExif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, it)
                }

                destExif.saveAttributes()
            }

            compressedBitmap.recycle()
            true
        } catch (e: Exception) {
            AppLogger.e("ImageUtils", e) { "Error saving image with EXIF data" }
            false
        }
    }

    /** Compresses and resizes an image bitmap */
    @SuppressLint("UseKtx")
    private fun compressImage(original: Bitmap): Bitmap {
        val width = original.width
        val height = original.height

        // Calculate the scaling factor
        val scaleFactor =
            if (width > height) {
                if (width > MAX_IMAGE_SIZE) MAX_IMAGE_SIZE.toFloat() / width else 1f
            } else {
                if (height > MAX_IMAGE_SIZE) MAX_IMAGE_SIZE.toFloat() / height else 1f
            }

        return if (scaleFactor < 1f) {
            val newWidth = (width * scaleFactor).toInt()
            val newHeight = (height * scaleFactor).toInt()
            original.scale(newWidth, newHeight)
        } else {
            original
        }
    }

    /** Gets the full file path for an image */
    fun getImageFile(context: Context, relativePath: String): File {
        // If relativePath already contains the directory, use it as-is
        // Otherwise, assume it's just a filename and add the images directory
        return if (relativePath.contains("/")) {
            File(context.filesDir, relativePath)
        } else {
            // Just a filename - look in the images directory
            File(getImagesDirectory(context), relativePath)
        }
    }

    /** Deletes an image file */
    fun deleteImage(context: Context, relativePath: String): Boolean {
        return try {
            val file = getImageFile(context, relativePath)
            file.delete()
        } catch (e: Exception) {
            AppLogger.e("ImageUtils", e) { "Failed to delete image: $relativePath" }
            false
        }
    }

    /** Imports an image file from the import directory */
    fun importImageFile(context: Context, sourceFile: File): String? {
        return try {
            if (!sourceFile.exists()) return null

            // Generate new filename to avoid conflicts
            val extension = sourceFile.extension.ifEmpty { "jpg" }
            val filename = "${UUID.randomUUID()}.$extension"
            val destFile = File(getImagesDirectory(context), filename)

            sourceFile.copyTo(destFile, overwrite = true)
            "$IMAGES_DIR/$filename"
        } catch (e: Exception) {
            AppLogger.e("ImageUtils", e) { "Failed to import image from source: ${sourceFile.name}" }
            null
        }
    }

    /** Gets all image files in the images directory */
    fun getAllImages(context: Context): List<String> {
        return try {
            val imagesDir = getImagesDirectory(context)
            imagesDir.listFiles()?.mapNotNull { file ->
                if (file.isFile &&
                    (
                        file.extension == "jpg" ||
                            file.extension == "jpeg" ||
                            file.extension == "png"
                        )
                ) {
                    "$IMAGES_DIR/${file.name}"
                } else {
                    null
                }
            }
                ?: emptyList()
        } catch (e: Exception) {
            AppLogger.e("ImageUtils", e) { "Failed to enumerate images directory" }
            emptyList()
        }
    }

    /** Temporarily saves an image during selection process */
    fun saveTemporaryImageFromUri(context: Context, imageUri: Uri): String? {
        return try {
            val source = ImageDecoder.createSource(context.contentResolver, imageUri)
            val originalBitmap = ImageDecoder.decodeBitmap(source)

            val tempFilename = "temp_${UUID.randomUUID()}.jpg"
            val imageFile = File(getImagesDirectory(context), tempFilename)

            val success = saveImageWithExif(context, imageUri, originalBitmap, imageFile)
            originalBitmap.recycle()

            if (!success) return null

            tempFilename
        } catch (e: Exception) {
            AppLogger.e("ImageUtils", e) { "Error saving temporary image from URI" }
            null
        }
    }

    /** Renames a temporary image */
    fun renameTemporaryImage(
        context: Context,
        tempFilename: String,
        problemId: String,
        imageIndex: Int,
    ): String? {
        return try {
            val tempFile = File(getImagesDirectory(context), tempFilename)
            if (!tempFile.exists()) {
                AppLogger.e("ImageUtils") { "Temporary file does not exist: $tempFilename" }
                return null
            }

            val deterministicFilename =
                ImageNamingUtils.generateImageFilename(problemId, imageIndex)
            val finalFile = File(getImagesDirectory(context), deterministicFilename)

            if (tempFile.renameTo(finalFile)) {
                AppLogger.d("ImageUtils") { "Renamed temporary image: $tempFilename -> $deterministicFilename" }
                deterministicFilename
            } else {
                AppLogger.e("ImageUtils") { "Failed to rename temporary image: $tempFilename" }
                null
            }
        } catch (e: Exception) {
            AppLogger.e("ImageUtils", e) { "Error renaming temporary image" }
            null
        }
    }

    /** Saves image data with a specific filename */
    fun saveImageFromBytesWithFilename(
        context: Context,
        imageData: ByteArray,
        filename: String,
    ): String? {
        return try {
            val imageFile = File(getImagesDirectory(context), filename)

            // Check if image is too large and needs compression
            if (imageData.size > 5 * 1024 * 1024) { // 5MB threshold
                // For large images, decode, compress, and try to preserve EXIF
                val bitmap =
                    BitmapFactory.decodeByteArray(imageData, 0, imageData.size) ?: return null
                val compressedBitmap = compressImage(bitmap)

                // Save compressed image
                FileOutputStream(imageFile).use { output ->
                    compressedBitmap.compress(Bitmap.CompressFormat.JPEG, IMAGE_QUALITY, output)
                }

                // Try to preserve EXIF orientation from original data
                try {
                    val originalExif = ExifInterface(java.io.ByteArrayInputStream(imageData))
                    val destExif = ExifInterface(imageFile.absolutePath)
                    val orientationValue = originalExif.getAttribute(ExifInterface.TAG_ORIENTATION)
                    orientationValue?.let {
                        destExif.setAttribute(ExifInterface.TAG_ORIENTATION, it)
                    }
                    destExif.saveAttributes()
                } catch (e: Exception) {
                    // If EXIF preservation fails, continue without it
                    AppLogger.w("ImageUtils") { "Failed to preserve EXIF data: ${e.message}" }
                }

                bitmap.recycle()
                compressedBitmap.recycle()
            } else {
                // For smaller images, save raw data to preserve all EXIF information
                FileOutputStream(imageFile).use { output -> output.write(imageData) }
            }

            // Return relative path
            "$IMAGES_DIR/$filename"
        } catch (e: Exception) {
            AppLogger.e("ImageUtils", e) { "Failed to save image from bytes: $filename" }
            null
        }
    }

    /** Cleans up orphaned images that are not referenced by any problems */
    fun cleanupOrphanedImages(context: Context, referencedPaths: Set<String>) {
        try {
            val allImages = getAllImages(context)
            val orphanedImages = allImages.filter { it !in referencedPaths }

            orphanedImages.forEach { path -> deleteImage(context, path) }
        } catch (e: Exception) {
            AppLogger.e("ImageUtils", e) { "Failed to clean up orphaned images" }
        }
    }
}
