package ch.threema.app.backuprestore

import java.io.File
import java.io.FilterInputStream
import java.io.IOException
import java.io.InputStream
import net.lingala.zip4j.ZipFile
import net.lingala.zip4j.exception.ZipException
import net.lingala.zip4j.model.FileHeader

/**
 * The [ZipFile] class has the problem that whenever [ZipFile.getInputStream] is called, it keeps a reference to the created input stream internally.
 * This is essentially a memory-leak, as these references aren't needed for anything other than being able to close all streams when calling
 * [ZipFile.close], which in our case is unnecessary as we already close them pro-actively. The [ZipFileWrapper] class comes in as a
 * helper to this problem, by ensuring that [ZipFile.close] is called pro-actively as well, making it remove the unnecessary internal reference.
 * The [ZipFile] instance can still be used normally afterwards.
 */
class ZipFileWrapper(
    file: File,
    password: String,
) {
    private val zipFile = ZipFile(file, password.toCharArray())

    fun isValidZipFile() =
        zipFile.isValidZipFile

    @Throws(ZipException::class)
    fun getFileHeaders(): List<FileHeader> =
        zipFile.fileHeaders

    fun getInputStream(fileHeader: FileHeader): InputStream =
        CloseCallbackInputStream(
            inputStream = zipFile.getInputStream(fileHeader),
            onClosed = {
                try {
                    zipFile.close()
                } catch (e: IOException) {
                    // should never happen, but if it does, we'd rather ignore it than cause a scene
                }
            },
        )

    private class CloseCallbackInputStream(
        inputStream: InputStream,
        private val onClosed: () -> Unit,
    ) : FilterInputStream(inputStream) {
        override fun close() {
            super.close()
            onClosed()
        }
    }
}
