package com.ustadmobile.libcache.response

import com.ustadmobile.ihttp.headers.IHttpHeaders
import com.ustadmobile.ihttp.headers.iHeadersBuilder
import com.ustadmobile.libcache.headers.addIntegrity
import com.ustadmobile.libcache.headers.containsHeader
import com.ustadmobile.libcache.headers.integrity
import com.ustadmobile.libcache.integrity.sha256Integrity
import com.ustadmobile.libcache.io.lastModified
import com.ustadmobile.libcache.io.useAndReadSha256
import com.ustadmobile.ihttp.request.IHttpRequest
import com.ustadmobile.ihttp.response.IHttpResponse
import kotlinx.atomicfu.atomic
import kotlinx.io.IOException
import kotlinx.io.Source
import kotlinx.io.buffered
import kotlinx.io.files.FileSystem
import kotlinx.io.files.Path
import kotlin.time.Clock
import kotlin.time.ExperimentalTime

@OptIn(ExperimentalTime::class)
class HttpPathResponse(
    private val path: Path,
    private val fileSystem: FileSystem,
    mimeType: String,
    override val request: IHttpRequest,
    integrity: String? = null,
    extraHeaders: IHttpHeaders? = null,
): IHttpResponse {

    private val bodyRead = atomic(false)

    override val headers: IHttpHeaders

    init {
        val metadata = fileSystem.metadataOrNull(path)
            ?: throw IOException("Cannot read from path")
        headers = iHeadersBuilder {
            header("Content-Length", metadata.size.toString())
            header("Content-Type", mimeType)
            header("Accept-Ranges", "bytes")
            if(extraHeaders?.containsHeader("age") != true) {
                header(
                    "Age",
                    (Clock.System.now().toEpochMilliseconds() - fileSystem.lastModified(path)).toString()
                )
            }

            val effectiveIntegrity = integrity ?:
                extraHeaders?.integrity() ?:
                sha256Integrity(fileSystem.source(path).buffered().useAndReadSha256())

            extraHeaders?.also { takeFrom(it) }
            addIntegrity(extraHeaders, effectiveIntegrity)
        }

    }

    override val responseCode: Int
        get() = 200

    override fun bodyAsSource(): Source {
        if(!bodyRead.getAndSet(true)) {
            return fileSystem.source(path).buffered()
        }else {
            throw IllegalStateException("Body has already been read")
        }
    }


}