/*
 * Episteme Reader - A native Android document reader.
 * Copyright (C) 2026 Episteme
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * mail: epistemereader@gmail.com
 */
package com.aryan.reader.paginatedreader

import timber.log.Timber
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import java.io.File
import java.security.MessageDigest

private fun getCacheKeyForFont(bookId: String, fontPath: String): String {
    val identifier = "$bookId:$fontPath"
    val digest = MessageDigest.getInstance("MD5").digest(identifier.toByteArray())
    return digest.joinToString("") { "%02x".format(it) } + ".ttf"
}

/**
 * Loads custom font faces defined in the EPUB's CSS into a map of [FontFamily] objects.
 * It handles WOFF2 fonts by converting them to TTF and storing them in a global, persistent cache.
 */
fun loadFontFamilies(fontFaces: List<FontFaceInfo>, extractionPath: String): Map<String, FontFamily> {
    if (fontFaces.isEmpty()) {
        return emptyMap()
    }
    Timber.d("Loading ${fontFaces.size} font faces from extraction path: $extractionPath")

    // 1. Define a stable, global font cache directory.
    // This assumes the parent of the extraction path is a stable base directory for epubs.
    val baseCacheDir = File(extractionPath).parentFile ?: return emptyMap()
    val fontCacheDir = File(baseCacheDir, "font_cache")
    if (!fontCacheDir.exists()) {
        fontCacheDir.mkdirs()
    }

    // 2. Get a stable identifier for the book from the extraction path.
    // e.g., "d0e205bf-65cc-4ab4-93cc-cd2d613a7bb3.epub" from a longer temp path.
    val bookId = File(extractionPath).name.substringBeforeLast("_")

    val fontsByFamily = fontFaces.groupBy {
        it.fontFamily.trim().removeSurrounding("'").removeSurrounding("\"").lowercase()
    }
    Timber.d("Grouped font faces by normalized family: ${fontsByFamily.keys}")

    return fontsByFamily.mapValues { (familyName, fontInfos) ->
        val fontList = fontInfos.mapNotNull { fontInfo ->
            try {
                Timber.d("Attempting to load font '$familyName' from resolved src path: '${fontInfo.src}'")
                var fontFile = File(extractionPath, fontInfo.src)

                if (!fontFile.exists()) {
                    Timber.w("Font file not found at: ${fontFile.absolutePath}")
                    return@mapNotNull null
                }

                // Handle WOFF2 conversion and global caching
                if (fontFile.extension.equals("woff2", ignoreCase = true)) {
                    // 3. Generate a unique, deterministic cache key for the font.
                    val cacheKey = getCacheKeyForFont(bookId, fontInfo.src)
                    val cachedTtfFile = File(fontCacheDir, cacheKey)

                    if (cachedTtfFile.exists()) {
                        // Use the globally cached TTF file if it exists
                        fontFile = cachedTtfFile
                        Timber.d("Using globally cached TTF for '${fontInfo.src}'")
                    } else {
                        // Convert and save the TTF to the global cache if it doesn't exist
                        Timber.d("Converting woff2 font: ${fontFile.name}")
                        val woff2Data = fontFile.readBytes()
                        val ttfData = Woff2Converter.convertWoff2ToTtf(woff2Data)

                        if (ttfData != null) {
                            cachedTtfFile.writeBytes(ttfData)
                            fontFile = cachedTtfFile // Use the newly created TTF file
                            Timber.d("Successfully converted and globally cached woff2 as '${cachedTtfFile.name}'")
                        } else {
                            Timber.e("Failed to convert woff2 font: ${fontFile.name}")
                            return@mapNotNull null
                        }
                    }
                }

                Font(
                    fontFile,
                    fontInfo.fontWeight ?: FontWeight.Normal,
                    fontInfo.fontStyle ?: FontStyle.Normal
                )
            } catch (e: Exception) {
                Timber.e(e, "Error loading font: ${fontInfo.src}")
                null
            }
        }

        if (fontList.isNotEmpty()) {
            Timber.d("Loaded family '$familyName' with ${fontList.size} font styles.")
            FontFamily(fontList)
        } else {
            Timber.w("Could not load any font styles for family '$familyName'.")
            null
        }
    }.filterValues { it != null }.mapValues { it.value!! }
}
