package de.krisenvorrat.app.data.repository

import de.krisenvorrat.app.domain.model.VersionInfo
import io.ktor.client.HttpClient
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.respond
import io.ktor.client.engine.mock.respondError
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.http.headersOf
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.io.File

class UpdateRepositoryImplTest {

    private val jsonSerializer = Json {
        ignoreUnknownKeys = true
        encodeDefaults = true
    }

    private lateinit var httpClient: HttpClient
    private lateinit var repository: UpdateRepositoryImpl

    @After
    fun tearDown() {
        if (::httpClient.isInitialized) {
            httpClient.close()
        }
    }

    private fun createClient(engine: MockEngine): HttpClient = HttpClient(engine) {
        install(ContentNegotiation) {
            json(jsonSerializer)
        }
    }

    // --- checkForUpdate ---

    @Test
    fun test_checkForUpdate_serverReturnsVersionInfo_returnsSuccess() = runTest {
        // Given
        val versionInfo = VersionInfo(versionCode = 5, versionName = "2.0", apkUrl = "https://example.com/app.apk")
        val engine = MockEngine { request ->
            assertEquals("/api/version", request.url.encodedPath)
            assertEquals(HttpMethod.Get, request.method)
            respond(
                content = jsonSerializer.encodeToString(VersionInfo.serializer(), versionInfo),
                status = HttpStatusCode.OK,
                headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString())
            )
        }
        httpClient = createClient(engine)
        repository = UpdateRepositoryImpl(httpClient)

        // When
        val result = repository.checkForUpdate("https://example.com")

        // Then
        assertTrue(result.isSuccess)
        assertEquals(versionInfo, result.getOrNull())
    }

    @Test
    fun test_checkForUpdate_serverReturns500_returnsFailure() = runTest {
        // Given
        val engine = MockEngine {
            respondError(HttpStatusCode.InternalServerError)
        }
        httpClient = createClient(engine)
        repository = UpdateRepositoryImpl(httpClient)

        // When
        val result = repository.checkForUpdate("https://example.com")

        // Then
        assertTrue(result.isFailure)
        assertTrue(result.exceptionOrNull()?.message?.contains("500") == true)
    }

    @Test
    fun test_checkForUpdate_serverUrlWithTrailingSlash_stripsSlash() = runTest {
        // Given
        val versionInfo = VersionInfo(versionCode = 5, versionName = "2.0", apkUrl = "https://example.com/app.apk")
        val engine = MockEngine { request ->
            assertEquals("/api/version", request.url.encodedPath)
            respond(
                content = jsonSerializer.encodeToString(VersionInfo.serializer(), versionInfo),
                status = HttpStatusCode.OK,
                headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString())
            )
        }
        httpClient = createClient(engine)
        repository = UpdateRepositoryImpl(httpClient)

        // When
        val result = repository.checkForUpdate("https://example.com/")

        // Then
        assertTrue(result.isSuccess)
    }

    // --- downloadApk ---

    @Test
    fun test_downloadApk_serverReturnsData_writesFileAndReportsProgress() = runTest {
        // Given
        val apkData = ByteArray(1024) { it.toByte() }
        val engine = MockEngine {
            respond(
                content = apkData,
                status = HttpStatusCode.OK,
                headers = headersOf(HttpHeaders.ContentLength, apkData.size.toString())
            )
        }
        httpClient = createClient(engine)
        repository = UpdateRepositoryImpl(httpClient)

        val targetFile = File.createTempFile("test-apk", ".apk")
        targetFile.deleteOnExit()
        val progressValues = mutableListOf<Float>()

        // When
        val result = repository.downloadApk(
            apkUrl = "https://example.com/app.apk",
            targetFile = targetFile,
            onProgress = { progressValues.add(it) }
        )

        // Then
        assertTrue(result.isSuccess)
        assertEquals(targetFile, result.getOrNull())
        assertTrue(targetFile.exists())
        assertEquals(apkData.size.toLong(), targetFile.length())
        assertTrue(progressValues.isNotEmpty())
        assertEquals(1f, progressValues.last(), 0.001f)
    }

    @Test
    fun test_downloadApk_serverReturns404_returnsFailure() = runTest {
        // Given
        val engine = MockEngine {
            respondError(HttpStatusCode.NotFound)
        }
        httpClient = createClient(engine)
        repository = UpdateRepositoryImpl(httpClient)

        val targetFile = File.createTempFile("test-apk", ".apk")
        targetFile.deleteOnExit()

        // When
        val result = repository.downloadApk(
            apkUrl = "https://example.com/app.apk",
            targetFile = targetFile,
            onProgress = {}
        )

        // Then
        assertTrue(result.isFailure)
    }

    @Test
    fun test_downloadApk_createsParentDirectories() = runTest {
        // Given
        val apkData = ByteArray(64) { it.toByte() }
        val engine = MockEngine {
            respond(
                content = apkData,
                status = HttpStatusCode.OK,
                headers = headersOf(HttpHeaders.ContentLength, apkData.size.toString())
            )
        }
        httpClient = createClient(engine)
        repository = UpdateRepositoryImpl(httpClient)

        val tempDir = File(System.getProperty("java.io.tmpdir"), "test-update-${System.nanoTime()}")
        val targetFile = File(tempDir, "subdir/app-latest.apk")

        try {
            // When
            val result = repository.downloadApk(
                apkUrl = "https://example.com/app.apk",
                targetFile = targetFile,
                onProgress = {}
            )

            // Then
            assertTrue(result.isSuccess)
            assertTrue(targetFile.exists())
        } finally {
            tempDir.deleteRecursively()
        }
    }
}
