package com.tt.honeyDue.network

import io.ktor.client.*
import io.ktor.client.engine.mock.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class HttpClientPluginsTest {

    // ==================== Retry Logic Tests ====================

    @Test
    fun testRetryTriggersOn500() = runTest {
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    if (requestCount < 3) {
                        respond(
                            content = """{"error":"Internal Server Error"}""",
                            status = HttpStatusCode.InternalServerError,
                            headers = headersOf(HttpHeaders.ContentType, "application/json")
                        )
                    } else {
                        respond(
                            content = """{"status":"ok"}""",
                            status = HttpStatusCode.OK,
                            headers = headersOf(HttpHeaders.ContentType, "application/json")
                        )
                    }
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value in 500..599
                }
                constantDelay(millis = 1, randomizationMs = 0)
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.OK, response.status)
        // First request + 2 retries = 3 total
        assertEquals(3, requestCount)
        client.close()
    }

    @Test
    fun testRetryTriggersOn503() = runTest {
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    if (requestCount < 2) {
                        respond(
                            content = """{"error":"Service Unavailable"}""",
                            status = HttpStatusCode.ServiceUnavailable,
                            headers = headersOf(HttpHeaders.ContentType, "application/json")
                        )
                    } else {
                        respond(
                            content = """{"status":"ok"}""",
                            status = HttpStatusCode.OK,
                            headers = headersOf(HttpHeaders.ContentType, "application/json")
                        )
                    }
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value in 500..599
                }
                constantDelay(millis = 1, randomizationMs = 0)
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.OK, response.status)
        assertEquals(2, requestCount)
        client.close()
    }

    @Test
    fun testNoRetryOn400() = runTest {
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    respond(
                        content = """{"error":"Bad Request"}""",
                        status = HttpStatusCode.BadRequest,
                        headers = headersOf(HttpHeaders.ContentType, "application/json")
                    )
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value in 500..599
                }
                constantDelay(millis = 1, randomizationMs = 0)
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.BadRequest, response.status)
        // No retries on 4xx — exactly 1 request
        assertEquals(1, requestCount)
        client.close()
    }

    @Test
    fun testNoRetryOn401() = runTest {
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    respond(
                        content = """{"error":"Unauthorized"}""",
                        status = HttpStatusCode.Unauthorized,
                        headers = headersOf(HttpHeaders.ContentType, "application/json")
                    )
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value in 500..599
                }
                constantDelay(millis = 1, randomizationMs = 0)
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.Unauthorized, response.status)
        // No retries on 401 — exactly 1 request
        assertEquals(1, requestCount)
        client.close()
    }

    @Test
    fun testNoRetryOn404() = runTest {
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    respond(
                        content = """{"error":"Not Found"}""",
                        status = HttpStatusCode.NotFound,
                        headers = headersOf(HttpHeaders.ContentType, "application/json")
                    )
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value in 500..599
                }
                constantDelay(millis = 1, randomizationMs = 0)
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.NotFound, response.status)
        assertEquals(1, requestCount)
        client.close()
    }

    @Test
    fun testNoRetryOn422() = runTest {
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    respond(
                        content = """{"error":"Unprocessable Entity"}""",
                        status = HttpStatusCode.UnprocessableEntity,
                        headers = headersOf(HttpHeaders.ContentType, "application/json")
                    )
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value in 500..599
                }
                constantDelay(millis = 1, randomizationMs = 0)
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.UnprocessableEntity, response.status)
        assertEquals(1, requestCount)
        client.close()
    }

    // ==================== 429 Rate Limit Handling Tests ====================

    @Test
    fun testRetryTriggersOn429() = runTest {
        // 429 responses should trigger retries just like 5xx errors
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    if (requestCount < 2) {
                        respond(
                            content = """{"error":"Too many requests. Please try again later."}""",
                            status = HttpStatusCode.TooManyRequests,
                            headers = headersOf(
                                HttpHeaders.ContentType to listOf("application/json"),
                                "Retry-After" to listOf("1")
                            )
                        )
                    } else {
                        respond(
                            content = """{"status":"ok"}""",
                            status = HttpStatusCode.OK,
                            headers = headersOf(HttpHeaders.ContentType, "application/json")
                        )
                    }
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value in 500..599 || response.status.value == 429
                }
                exponentialDelay(base = 1.0, maxDelayMs = 10)
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.OK, response.status)
        // First request (429) + 1 retry (200) = 2 total
        assertEquals(2, requestCount)
        client.close()
    }

    @Test
    fun testRetryOn429UsesRetryAfterHeader() = runTest {
        // Verify the Retry-After header is accessible in the retry config.
        // We can't easily measure actual delay in a unit test, but we can
        // verify the header is read and the retry happens.
        var requestCount = 0
        var retryAfterValue: Long? = null
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    if (requestCount < 2) {
                        respond(
                            content = """{"error":"Too many requests. Please try again later."}""",
                            status = HttpStatusCode.TooManyRequests,
                            headers = headersOf(
                                HttpHeaders.ContentType to listOf("application/json"),
                                "Retry-After" to listOf("30")
                            )
                        )
                    } else {
                        respond(
                            content = """{"status":"ok"}""",
                            status = HttpStatusCode.OK,
                            headers = headersOf(HttpHeaders.ContentType, "application/json")
                        )
                    }
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value == 429
                }
                delayMillis { _ ->
                    retryAfterValue = 30L // Simulate reading Retry-After
                    1L
                }
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.OK, response.status)
        assertEquals(2, requestCount)
        // Verify the Retry-After header value was read
        assertEquals(30L, retryAfterValue)
        client.close()
    }

    @Test
    fun test429WithoutRetryAfterDefaultsTo5Seconds() = runTest {
        // When the server doesn't send a Retry-After header, the delay
        // should default to 5 seconds (5000ms).
        var retryAfterValue: Long? = null
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    if (requestCount < 2) {
                        respond(
                            content = """{"error":"Too many requests. Please try again later."}""",
                            status = HttpStatusCode.TooManyRequests,
                            headers = headersOf(HttpHeaders.ContentType, "application/json")
                            // No Retry-After header
                        )
                    } else {
                        respond(
                            content = """{"status":"ok"}""",
                            status = HttpStatusCode.OK,
                            headers = headersOf(HttpHeaders.ContentType, "application/json")
                        )
                    }
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 3
                retryIf { _, response ->
                    response.status.value == 429
                }
                delayMillis { _ ->
                    retryAfterValue = 5L // Default when no Retry-After header
                    1L
                }
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.OK, response.status)
        // Default should be 5 seconds when no Retry-After header
        assertEquals(5L, retryAfterValue)
        client.close()
    }

    @Test
    fun test429ExhaustsRetriesThenReturns429() = runTest {
        // If all retries are exhausted on 429, the final 429 response is returned
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    respond(
                        content = """{"error":"Too many requests. Please try again later."}""",
                        status = HttpStatusCode.TooManyRequests,
                        headers = headersOf(
                            HttpHeaders.ContentType to listOf("application/json"),
                            "Retry-After" to listOf("60")
                        )
                    )
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            install(HttpRequestRetry) {
                maxRetries = 2
                retryIf { _, response ->
                    response.status.value == 429
                }
                delayMillis { _ -> 1L } // Minimal delay for test speed
            }
        }

        val response = client.get("https://example.com/test")
        assertEquals(HttpStatusCode.TooManyRequests, response.status)
        // 1 initial + 2 retries = 3 total
        assertEquals(3, requestCount)
        client.close()
    }

    // ==================== Token Refresh / 401 Handling Tests ====================

    @Test
    fun testTokenExpiredExceptionIsRefreshed() {
        val exception = TokenExpiredException(refreshed = true)
        assertTrue(exception.refreshed)
        assertTrue(exception.message!!.contains("refreshed"))
    }

    @Test
    fun testTokenExpiredExceptionNotRefreshed() {
        val exception = TokenExpiredException(refreshed = false)
        assertTrue(!exception.refreshed)
        assertTrue(exception.message!!.contains("re-authenticate"))
    }

    @Test
    fun test401WithNonExpiredTokenDoesNotTriggerRefresh() = runTest {
        // A 401 that does NOT contain "expired" or "token_expired" should NOT
        // throw TokenExpiredException — it should just return the 401 response.
        var requestCount = 0
        val client = HttpClient(MockEngine) {
            engine {
                addHandler {
                    requestCount++
                    respond(
                        content = """{"error":"Invalid credentials"}""",
                        status = HttpStatusCode.Unauthorized,
                        headers = headersOf(HttpHeaders.ContentType, "application/json")
                    )
                }
            }
            install(ContentNegotiation) {
                json(Json { ignoreUnknownKeys = true })
            }
            // No response validator installed — simulates behavior where
            // the validator only triggers on "expired" keyword in body
        }

        val response = client.get("https://example.com/auth/login/")
        assertEquals(HttpStatusCode.Unauthorized, response.status)
        val body = response.bodyAsText()
        assertTrue(body.contains("Invalid credentials"))
        assertEquals(1, requestCount)
        client.close()
    }
}
