package com.aegir.demo

import android.content.Context
import java.io.File
import java.util.zip.ZipInputStream

/**
 * Bundled venues ship as a single venue_map_<ID>.zip asset (instead of loose
 * folders) to stay under the APK's 65,535 zip-entry limit. On first use the
 * zip is extracted to filesDir/venues/venue_map_<ID>/ and the map is served
 * from there via file:// URLs.
 */
object VenueAssets {

    private const val PREFIX = "venue_map_"
    private const val ZIP_EXT = ".zip"
    private const val MARKER = ".extracted"

    @JvmStatic
    fun zipAssetName(venueId: String): String = "$PREFIX$venueId$ZIP_EXT"

    @JvmStatic
    fun hasZipAsset(context: Context, venueId: String): Boolean = try {
        context.assets.list("")?.contains(zipAssetName(venueId)) == true
    } catch (e: Exception) {
        false
    }

    @JvmStatic
    fun extractedDir(context: Context, venueId: String): File =
        File(File(context.filesDir, "venues"), "$PREFIX$venueId")

    /**
     * Extracts the venue zip asset on first use; subsequent calls are no-ops.
     * Must be called off the main thread (large venues are several thousand files).
     */
    @JvmStatic
    fun ensureExtracted(context: Context, venueId: String): File {
        val dir = extractedDir(context, venueId)
        val marker = File(dir, MARKER)
        if (marker.exists()) {
            return dir
        }
        dir.deleteRecursively()
        if (!dir.mkdirs()) {
            throw IllegalStateException("Could not create venue extraction dir: $dir")
        }
        val rootPath = dir.canonicalPath + File.separator
        context.assets.open(zipAssetName(venueId)).use { input ->
            ZipInputStream(input.buffered()).use { zin ->
                var entry = zin.nextEntry
                while (entry != null) {
                    val out = File(dir, entry.name)
                    if (!out.canonicalPath.startsWith(rootPath)) {
                        throw SecurityException("Zip entry escapes extraction dir: ${entry.name}")
                    }
                    if (entry.isDirectory) {
                        out.mkdirs()
                    } else {
                        out.parentFile?.mkdirs()
                        out.outputStream().use { zin.copyTo(it) }
                    }
                    zin.closeEntry()
                    entry = zin.nextEntry
                }
            }
        }
        marker.createNewFile()
        return dir
    }
}
