package com.all.pdfreader.pro.app.room.repository

import android.content.Context
import androidx.room.Room
import com.all.pdfreader.pro.app.room.database.PdfDatabase
import com.all.pdfreader.pro.app.room.entity.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import java.security.MessageDigest

class PdfRepository private constructor(context: Context) {

    private val database = Room.databaseBuilder(
        context, PdfDatabase::class.java, PdfDatabase.DATABASE_NAME
    ).build()

    private val pdfDao = database.pdfDocumentDao()
    private val recentDao = database.recentReadDao()
    private val bookmarkDao = database.bookmarkDao()
    private val noteDao = database.noteDao()

    // PDF文档相关操作
    suspend fun insertOrUpdateDocument(document: PdfDocumentEntity) {
        pdfDao.insertOrUpdate(document)
    }

    suspend fun getDocumentByHash(fileHash: String): PdfDocumentEntity? {
        return pdfDao.getByHash(fileHash)
    }

    suspend fun getDocumentByPath(filePath: String): PdfDocumentEntity? {
        return pdfDao.getByPath(filePath)
    }

    suspend fun getAllDocumentsOnce(): List<PdfDocumentEntity> = pdfDao.getAllDocumentsOnce()

    fun getAllDocuments(): Flow<List<PdfDocumentEntity>> = pdfDao.getAllDocuments()
    fun getRecentlyOpenedDocuments(): Flow<List<PdfDocumentEntity>> =
        pdfDao.getRecentlyOpenedDocuments()

    fun getFavoriteDocuments(): Flow<List<PdfDocumentEntity>> = pdfDao.getFavoriteDocuments()

    /**
     * 1.用户输入 " " → 不会返回任何数据
     * 2.用户输入 " my file " → 会去掉两边空格，正确匹配包含 my file 的文件
     * 3.用户输入 "my" → 正常模糊匹配
     */
    fun searchDocuments(query: String): Flow<List<PdfDocumentEntity>> =
        pdfDao.searchDocuments(query)

    suspend fun updateFilePathAndFileName(
        oldFilePath: String,
        newFilePath: String,
        newName: String
    ) {
        pdfDao.updateFilePathAndFileName(oldFilePath, newFilePath, newName)
    }

    //语句更新可响应flow
    suspend fun updateIsPassword(filePath: String, hasPassword: Boolean) {
        pdfDao.updateIsPassword(filePath, hasPassword)
    }

    //更新最后打开时间，可以设置为0L，相当于更新成未打开过。
    suspend fun updateLastOpenTime(filePath: String, time: Long = 0L) {
        pdfDao.updateLastOpenTime(filePath, time)
    }

    suspend fun updateLastOpenTimes(filePaths: List<String>, time: Long = 0L) {
        pdfDao.updateLastOpenTimes(filePaths, time)
    }

    suspend fun updateFavoriteStatus(filePath: String, isFavorite: Boolean) {
        val document = pdfDao.getByPath(filePath)?.copy(
            isFavorite = isFavorite,
            addedToFavoriteTime = if (isFavorite) System.currentTimeMillis() else null
        )
        document?.let { pdfDao.update(it) }
    }

    //批量取消收藏
    suspend fun cancelFavorites(filePaths: List<String>) {
        pdfDao.cancelFavoriteStatus(filePaths)
    }

    suspend fun updateReadingProgress(filePath: String, page: Int, progress: Float) {
        val document = pdfDao.getByPath(filePath)?.copy(
            lastOpenedTime = System.currentTimeMillis(),
            lastReadPage = page,
            readingProgress = progress
        )
        document?.let { pdfDao.update(it) }
    }

    //复制更新不会更新flow
    suspend fun updatePasswordStatus(filePath: String, isPassword: Boolean) {
        val document = pdfDao.getByPath(filePath)?.copy(
            isPassword = isPassword
        )
        document?.let { pdfDao.update(it) }
    }

    suspend fun updatePassword(filePath: String, password: String?) {
        val document = pdfDao.getByPath(filePath)?.copy(
            password = password
        )
        document?.let { pdfDao.update(it) }
    }

    suspend fun updateThumbnailPath(filePath: String, path: String) {
        val document = pdfDao.getByPath(filePath)?.copy(
            thumbnailPath = path,
        )
        document?.let { pdfDao.update(it) }
    }

    // 最近阅读相关操作
    suspend fun addToRecent(filePath: String, page: Int = 0) {
        val existing = recentDao.getByFilePath(filePath)
        if (existing != null) {
            recentDao.updateOpenTime(filePath)
        } else {
            recentDao.insertOrUpdate(
                RecentReadEntity(
                    filePath = filePath, lastOpenedTime = System.currentTimeMillis()
                )
            )
        }
    }

    fun getRecentReadDocuments(): Flow<List<PdfDocumentEntity>> = recentDao.getRecentReadDocuments()

    // 书签相关操作
    suspend fun addBookmark(bookmark: BookmarkEntity): Long = bookmarkDao.insert(bookmark)
    suspend fun updateBookmark(bookmark: BookmarkEntity): Int = bookmarkDao.update(bookmark)
    suspend fun updateBookmarkFilePath(oldPath: String, newPath: String) =
        bookmarkDao.updateFilePath(oldPath, newPath)

    suspend fun deleteBookmark(bookmark: BookmarkEntity): Int = bookmarkDao.delete(bookmark)
    suspend fun deleteAllBookmark(filePath: String): Int {
        return bookmarkDao.deleteAllByPdf(filePath)
    }

    fun getBookmarksByPdf(filePath: String): Flow<List<BookmarkEntity>> =
        bookmarkDao.getBookmarksByPdf(filePath)

    suspend fun getBookmarksByPage(filePath: String, page: Int): List<BookmarkEntity> =
        bookmarkDao.getBookmarksByPage(filePath, page)

    // 注释相关操作
    suspend fun addNote(note: NoteEntity): Long = noteDao.insert(note)
    suspend fun updateNote(note: NoteEntity) = noteDao.update(note)
    suspend fun deleteNote(note: NoteEntity) = noteDao.delete(note)
    fun getNotesByPdf(filePath: String): Flow<List<NoteEntity>> = noteDao.getNotesByPdf(filePath)
    suspend fun getNotesByPage(filePath: String, page: Int): List<NoteEntity> =
        noteDao.getNotesByPage(filePath, page)

    fun getNotesByType(filePath: String, noteType: String): Flow<List<NoteEntity>> =
        noteDao.getNotesByType(filePath, noteType)

    // 组合查询
    suspend fun getPdfWithDetails(filePath: String): Flow<PdfDetails> {
        return combine(pdfDao.getByPath(filePath)?.let { kotlinx.coroutines.flow.flowOf(it) }
            ?: kotlinx.coroutines.flow.flowOf(null),
            bookmarkDao.getBookmarksByPdf(filePath),
            noteDao.getNotesByPdf(filePath)) { document, bookmarks, notes ->
            PdfDetails(document, bookmarks, notes)
        }
    }

    // 工具方法
    suspend fun generateFileHash(filePath: String): String {
        return try {
            val file = java.io.File(filePath)
            if (!file.exists()) return ""

            val digest = MessageDigest.getInstance("MD5")
            file.inputStream().use { input ->
                val buffer = ByteArray(8192)
                var bytes = input.read(buffer)
                while (bytes >= 0) {
                    digest.update(buffer, 0, bytes)
                    bytes = input.read(buffer)
                }
            }
            digest.digest().joinToString("") { "%02x".format(it) }
        } catch (e: Exception) {
            ""
        }
    }

    // 数据清理
    suspend fun deleteDocument(filePath: String) {
        pdfDao.deleteByPath(filePath)
        recentDao.deleteByPdfPath(filePath)
        bookmarkDao.deleteAllByPdf(filePath)
        noteDao.deleteAllByPdf(filePath)
    }

    // 数据清理集合
    suspend fun deleteDocuments(filePaths: List<String>) {
        pdfDao.deleteByPaths(filePaths)
        recentDao.deleteByPdfPaths(filePaths)
        bookmarkDao.deleteAllByPdfs(filePaths)
        noteDao.deleteAllByPdfs(filePaths)
    }

    companion object {
        @Volatile
        private var INSTANCE: PdfRepository? = null

        fun initialize(context: Context) {
            if (INSTANCE == null) {
                synchronized(this) {
                    if (INSTANCE == null) {
                        INSTANCE = PdfRepository(context.applicationContext)
                    }
                }
            }
        }

        fun getInstance(): PdfRepository {
            return INSTANCE
                ?: throw IllegalStateException("PdfRepository must be initialized first")
        }

        // 向后兼容的方法
        fun getInstance(context: Context): PdfRepository {
            return getInstance()
        }
    }
}

data class PdfDetails(
    val document: PdfDocumentEntity?,
    val bookmarks: List<BookmarkEntity>,
    val notes: List<NoteEntity>
)