package com.all.pdfreader.pro.app.util

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.os.ParcelFileDescriptor
import android.util.Log
import androidx.core.graphics.createBitmap
import com.all.pdfreader.pro.app.model.PdfPageItem
import com.shockwave.pdfium.PdfiumCore
import com.tom_roush.pdfbox.io.MemoryUsageSetting
import com.tom_roush.pdfbox.multipdf.PDFMergerUtility
import com.tom_roush.pdfbox.pdmodel.PDDocument
import com.tom_roush.pdfbox.pdmodel.PDPage
import com.tom_roush.pdfbox.pdmodel.PDPageContentStream
import com.tom_roush.pdfbox.pdmodel.common.PDRectangle
import com.tom_roush.pdfbox.pdmodel.graphics.image.JPEGFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import androidx.core.graphics.scale

object PdfUtils {

    //拆分缩略图缓存地址
    const val child = "pdf_split_thumbs"

    //清除拆分缓存文件夹
    fun clearPdfThumbsCache(context: Context) {
        val cacheDir = File(context.cacheDir, child)
        if (cacheDir.exists()) {
            val ss = cacheDir.deleteRecursively()  // Kotlin 提供的递归删除方法
            Log.d("ocean", "clearPdfThumbsCache->$ss")
        }
    }

    /**
     * 分页渲染 PDF 并返回 Flow<PdfPageItem>
     * 先 emit 页对象（preview = null），UI 可立即显示总页数
     * 后续异步渲染 Bitmap 并更新对象
     */
    fun splitPdfToPageItemsFlow(
        context: Context,
        inputFile: File,
        dpi: Float = 72f,
        chunkSize: Int = 5,
        thumbWidth: Int = 200,
        password: String? = null
    ): Flow<PdfPageItem> = flow {
        val pdfiumCore = PdfiumCore(context)
        ParcelFileDescriptor.open(inputFile, ParcelFileDescriptor.MODE_READ_ONLY).use { fd ->
            val pdfDocument = if (password.isNullOrEmpty()) {
                pdfiumCore.newDocument(fd)
            } else {
                pdfiumCore.newDocument(fd, password)
            }

            val pageCount = pdfiumCore.getPageCount(pdfDocument)

            val pages = List(pageCount) {
                PdfPageItem(pageIndex = it, previewFilePath = null, isSelected = false)
            }

            // 先 emit 页对象，让 UI 知道总页数
            pages.forEach { emit(it) }

            val cacheDir = File(context.cacheDir, child).apply { mkdirs() }

            for (i in 0 until pageCount) {
                pdfiumCore.openPage(pdfDocument, i)

                val width = (pdfiumCore.getPageWidthPoint(pdfDocument, i) * dpi / 72).toInt()
                val height = (pdfiumCore.getPageHeightPoint(pdfDocument, i) * dpi / 72).toInt()

                val scale = thumbWidth.toFloat() / width
                val targetHeight = (height * scale).toInt()

                val bitmap = createBitmap(thumbWidth, targetHeight, Bitmap.Config.RGB_565)
                pdfiumCore.renderPageBitmap(pdfDocument, bitmap, i, 0, 0, thumbWidth, targetHeight)

                // 保存为压缩 JPEG
                val outFile = File(cacheDir, inputFile.name + "_page_$i.jpg")
                FileOutputStream(outFile).use { fos ->
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 70, fos)
                }
                bitmap.recycle()

                pages[i].previewFilePath = outFile.absolutePath
                emit(pages[i])

                if ((i + 1) % chunkSize == 0) delay(50) // 分批渲染，保证 UI 流畅
            }

            pdfiumCore.closeDocument(pdfDocument)
        }
    }.flowOn(Dispatchers.IO)

    /**
     * 导出选中的 PDF 页到一个新的 PDF 文件
     *
     * 使用 PDFBox 的 PDDocument 导出原 PDF 中选中的页，支持进度回调。
     * 避免直接使用缩略图生成 PDF，以保持原 PDF 的矢量质量。
     *
     * @param inputFile      原 PDF 文件
     * @param selectedPages  选中的页对象列表，每个对象包含 pageIndex
     * @param outputDir      输出目录，如果不存在会自动创建
     * @param outputFileName 输出文件名，例如 "selected.pdf"
     * @param onProgress     可选回调，当前处理进度 (current 页, total 页)
     * @return               新生成的 PDF 文件，失败返回 null
     */
    suspend fun exportSelectedPages(
        inputFile: File,
        selectedPages: List<PdfPageItem>,
        outputDir: File,
        outputFileName: String,
        onProgress: ((current: Int, total: Int) -> Unit)? = null,
        splitPassword: String? = null
    ): File? = withContext(Dispatchers.IO) {

        // 如果没有选中的页，直接返回 null
        if (selectedPages.isEmpty()) return@withContext null
        if (!outputDir.exists()) outputDir.mkdirs()
        val outputFile = File(outputDir, outputFileName)
        try {
            if (splitPassword.isNullOrEmpty()) {
                PDDocument.load(inputFile, MemoryUsageSetting.setupTempFileOnly()).use { document ->
                    PDDocument().use { newDocument ->
                        // 按页索引排序，保证顺序正确
                        val sortedPages = selectedPages.sortedBy { it.pageIndex }
                        val total = sortedPages.size//总数
                        sortedPages.forEachIndexed { index, pageItem ->
                            // 导入原文档的页面到新文档
                            newDocument.importPage(document.getPage(pageItem.pageIndex))
                            // 回调进度
                            onProgress?.invoke(index + 1, total)
                            delay(1)
                        }
                        // 保存新 PDF 文件
                        newDocument.save(outputFile)
                    }
                }
            } else {
                PDDocument.load(inputFile, splitPassword, MemoryUsageSetting.setupTempFileOnly())
                    .use { document ->
                        PDDocument().use { newDocument ->
                            // 按页索引排序，保证顺序正确
                            val sortedPages = selectedPages.sortedBy { it.pageIndex }
                            val total = sortedPages.size//总数
                            sortedPages.forEachIndexed { index, pageItem ->
                                // 导入原文档的页面到新文档
                                newDocument.importPage(document.getPage(pageItem.pageIndex))
                                // 回调进度
                                onProgress?.invoke(index + 1, total)
                                delay(1)
                            }
                            // 保存新 PDF 文件
                            newDocument.save(outputFile)
                        }
                    }
            }
            outputFile
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    /**
     * 合并多个 PDF 文件到一个新的 PDF 文件
     *
     * 使用 PDFBox 的 PDFMergerUtility 合并 PDF，支持进度回调。
     *
     * @param inputFiles     要合并的 PDF 文件列表
     * @param outputDir      输出目录，如果不存在会自动创建
     * @param outputFileName 输出文件名，例如 "merged.pdf"
     * @param onProgress     可选回调，当前处理进度 (current 文件, total 文件)
     * @return               新生成的 PDF 文件，失败返回 null
     */
    /**
     * 合并多个 PDF 文件到一个新的 PDF 文件
     *
     * 使用 PDFBox 的 PDFMergerUtility 合并 PDF，支持进度回调。
     *
     * @param inputFiles     要合并的 PDF 文件列表
     * @param outputDir      输出目录，如果不存在会自动创建
     * @param outputFileName 输出文件名，例如 "merged.pdf"
     * @param onProgress     可选回调，当前处理进度 (current 文件, total 文件)
     * @return               新生成的 PDF 文件，失败返回 null
     */
    suspend fun mergePdfFilesWithPassword(
        inputFiles: List<Pair<File, String?>>, // Pair<文件, 密码>
        outputDir: File,
        outputFileName: String,
        onProgress: ((current: Int, total: Int) -> Unit)? = null
    ): File? = withContext(Dispatchers.IO) {

        if (inputFiles.isEmpty()) return@withContext null
        if (!outputDir.exists()) outputDir.mkdirs()

        val outputFile = File(outputDir, outputFileName)

        try {
            val merger = PDFMergerUtility()
            val total = 100

            // 0~30%: 加载 PDF
            inputFiles.forEachIndexed { index, (file, password) ->
                val doc = if (!password.isNullOrEmpty()) {
                    PDDocument.load(file, password)
                } else {
                    PDDocument.load(file)
                }

                // 移除加密保护
                if (doc.isEncrypted) {
                    doc.isAllSecurityToBeRemoved = true
                }

                // 将文档临时保存到内存文件，然后添加给 merger
                val tempFile = File.createTempFile("merge_", ".pdf")
                doc.save(tempFile)
                doc.close()

                merger.addSource(tempFile)

                val progress = ((index + 1).toFloat() / inputFiles.size * 30).toInt()
                onProgress?.invoke(progress, total)
                delay(50)
            }

            merger.destinationFileName = outputFile.absolutePath

            // 伪进度 30~95%
            var fakeProgress = 30
            val fakeJob = launch {
                while (fakeProgress < 95) {
                    fakeProgress += (1..3).random()
                    if (fakeProgress > 95) fakeProgress = 95
                    onProgress?.invoke(fakeProgress, total)
                    delay((100L..300L).random())
                }
            }

            // 真正合并
            merger.mergeDocuments(MemoryUsageSetting.setupTempFileOnly())

            fakeJob.cancel()
            onProgress?.invoke(100, total)

            outputFile
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    /**
     * 图片转 PDF（优化版，安全、快速、低内存）
     */
    suspend fun imgToPdfFilesSafe(
        inputFiles: List<File>,
        outputDir: File,
        outputFileName: String,
        onProgress: ((current: Int, total: Int) -> Unit)? = null
    ): File? = withContext(Dispatchers.IO) {
        if (inputFiles.isEmpty()) return@withContext null

        val outputFile = File(outputDir, outputFileName)
        if (!outputDir.exists()) outputDir.mkdirs()

        var document: PDDocument? = null
        try {
            document = PDDocument()
            val total = inputFiles.size

            inputFiles.forEachIndexed { index, imageFile ->
                try {
                    //安全加载压缩后的图片
                    val options = BitmapFactory.Options().apply {
                        inPreferredConfig = Bitmap.Config.RGB_565
                    }
                    val bitmap = BitmapFactory.decodeFile(imageFile.absolutePath, options)
                        ?: return@forEachIndexed

                    //按 A4 比例缩放
                    val a4Width = PDRectangle.A4.width
                    val a4Height = PDRectangle.A4.height
                    val imageRatio = bitmap.width.toFloat() / bitmap.height
                    val pageRatio = a4Width / a4Height

                    val targetWidth: Float
                    val targetHeight: Float
                    if (imageRatio > pageRatio) {
                        targetWidth = a4Width
                        targetHeight = a4Width / imageRatio
                    } else {
                        targetHeight = a4Height
                        targetWidth = a4Height * imageRatio
                    }

                    val offsetX = (a4Width - targetWidth) / 2
                    val offsetY = (a4Height - targetHeight) / 2

                    val page = PDPage(PDRectangle.A4)
                    document.addPage(page)

                    val pdImage = JPEGFactory.createFromImage(document, bitmap)
                    PDPageContentStream(document, page).use { contentStream ->
                        contentStream.drawImage(
                            pdImage, offsetX, offsetY, targetWidth, targetHeight
                        )
                    }

                    bitmap.recycle()

                } catch (e: OutOfMemoryError) {
                    e.printStackTrace()
                    System.gc() // 手动GC一次（极端情况下）
                    return@forEachIndexed
                } catch (e: Exception) {
                    e.printStackTrace()
                }

                onProgress?.invoke(index + 1, total)
            }

            document.save(outputFile)
            outputFile

        } catch (e: Exception) {
            e.printStackTrace()
            null
        } finally {
            document?.close()
        }
    }

    /**
     * 导出选中的 PDF 页到图片文件（支持加密、进度回调、防重名、格式可选）
     *
     * @param context 上下文
     * @param inputFile 原 PDF 文件
     * @param selectedPages 选中的页对象列表（包含 pageIndex）
     * @param outputDir 输出目录
     * @param password PDF 密码（可为空）
     * @param format 输出格式："JPG" 或 "PNG"
     * @param quality 图片质量（仅对 JPG 有效，0~100）
     * @param onProgress 当前进度回调 (current 页, total 页)
     * @return 导出的图片文件列表
     */
    suspend fun exportSelectedPagesToImages(
        context: Context,
        inputFile: File,
        selectedPages: List<PdfPageItem>,
        outputDir: File,
        password: String? = null,
        format: String = "JPG",
        quality: Int = 100,
        onProgress: ((current: Int, total: Int) -> Unit)? = null
    ): List<File> = withContext(Dispatchers.IO) {

        if (selectedPages.isEmpty()) return@withContext emptyList()
        if (!outputDir.exists()) outputDir.mkdirs()

        val pdfiumCore = PdfiumCore(context)
        val fd = ParcelFileDescriptor.open(inputFile, ParcelFileDescriptor.MODE_READ_ONLY)
        val document = if (password.isNullOrEmpty()) {
            pdfiumCore.newDocument(fd)
        } else {
            pdfiumCore.newDocument(fd, password)
        }

        val sortedPages = selectedPages.sortedBy { it.pageIndex }
        val total = sortedPages.size
        val resultList = mutableListOf<File>()
        val baseName = inputFile.nameWithoutExtension

        val compressFormat = if (format.equals("PNG", ignoreCase = true)) {
            Bitmap.CompressFormat.PNG
        } else {
            Bitmap.CompressFormat.JPEG
        }
        val extension = if (format.equals("PNG", ignoreCase = true)) "png" else "jpg"

        sortedPages.forEachIndexed { index, pageItem ->
            try {
                pdfiumCore.openPage(document, pageItem.pageIndex)
                val width = pdfiumCore.getPageWidthPoint(document, pageItem.pageIndex)
                val height = pdfiumCore.getPageHeightPoint(document, pageItem.pageIndex)
                val bitmap = createBitmap(width, height)
                pdfiumCore.renderPageBitmap(document, bitmap, pageItem.pageIndex, 0, 0, width, height)

                // 防止重名
                var outFile = File(outputDir, "${baseName}_page_${pageItem.pageIndex + 1}.$extension")
                var counter = 1
                while (outFile.exists()) {
                    outFile = File(outputDir, "${baseName}_page_${pageItem.pageIndex + 1}($counter).$extension")
                    counter++
                }

                FileOutputStream(outFile).use {
                    bitmap.compress(compressFormat, quality, it)
                }
                resultList.add(outFile)

                onProgress?.invoke(index + 1, total)
                delay(1)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

        pdfiumCore.closeDocument(document)
        fd.close()
        resultList
    }

    /**
     * 导出选中的 PDF 页到长图文件
     */
    suspend fun exportPagesAsLongImage(
        context: Context,
        inputFile: File,
        selectedPages: List<PdfPageItem>,
        outputDir: File,
        password: String? = null,
        format: String = "JPG",
        quality: Int = 100,
        onProgress: ((current: Int, total: Int) -> Unit)? = null
    ): File? = withContext(Dispatchers.IO) {

        if (selectedPages.isEmpty()) return@withContext null
        if (!outputDir.exists()) outputDir.mkdirs()

        val pdfium = PdfiumCore(context)
        val fd = ParcelFileDescriptor.open(inputFile, ParcelFileDescriptor.MODE_READ_ONLY)
        val doc = if (password.isNullOrEmpty()) pdfium.newDocument(fd) else pdfium.newDocument(fd, password)

        val sorted = selectedPages.sortedBy { it.pageIndex }
        val bitmaps = mutableListOf<Bitmap>()

        sorted.forEachIndexed { idx, item ->
            pdfium.openPage(doc, item.pageIndex)
            val w = pdfium.getPageWidthPoint(doc, item.pageIndex)
            val h = pdfium.getPageHeightPoint(doc, item.pageIndex)
            val bmp = createBitmap(w, h)
            pdfium.renderPageBitmap(doc, bmp, item.pageIndex, 0, 0, w, h)

            bitmaps.add(bmp)
            onProgress?.invoke(idx + 1, sorted.size)
            delay(1)
        }

        val longBitmap = mergeBitmapsVertical(bitmaps)
        val baseName = inputFile.nameWithoutExtension
        val extension = if (format.equals("PNG", true)) "png" else "jpg"
        val compressType = if (format.equals("PNG", true)) Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG

        var outFile = File(outputDir, "${baseName}_long.$extension")
        var i = 1
        while (outFile.exists()) {
            outFile = File(outputDir, "${baseName}_long($i).$extension")
            i++
        }

        FileOutputStream(outFile).use {
            longBitmap.compress(compressType, quality, it)
        }

        pdfium.closeDocument(doc)
        fd.close()

        // 释放内存
        bitmaps.forEach { it.recycle() }

        return@withContext outFile
    }

    private fun mergeBitmapsVertical(bitmaps: List<Bitmap>): Bitmap {
        if (bitmaps.isEmpty()) throw IllegalArgumentException("Bitmap list is empty")

        // 统一宽度为最宽的
        val maxWidth = bitmaps.maxOf { it.width }

        // 按比例缩放每张，保持宽度一致
        val scaledBitmaps = bitmaps.map { bmp ->
            if (bmp.width == maxWidth) bmp
            else bmp.scale(maxWidth, (bmp.height * (maxWidth / bmp.width.toFloat())).toInt())
        }

        val totalHeight = scaledBitmaps.sumOf { it.height }
        val mergedBitmap = createBitmap(maxWidth, totalHeight)
        val canvas = Canvas(mergedBitmap)

        var currentHeight = 0
        scaledBitmaps.forEach { bmp ->
            canvas.drawBitmap(bmp, 0f, currentHeight.toFloat(), null)
            currentHeight += bmp.height
        }

        return mergedBitmap
    }

}
