package com.fabledsword.minstrel.api

import com.fabledsword.minstrel.auth.AuthStore
import com.fabledsword.minstrel.cache.db.dao.AuthSessionDao
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Protocol
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

@OptIn(ExperimentalCoroutinesApi::class)
class BaseUrlInterceptorTest {
    private lateinit var server: MockWebServer
    private lateinit var authStore: AuthStore

    @BeforeEach
    fun setup() {
        server = MockWebServer().apply { start() }
        val dao =
            mockk<AuthSessionDao> {
                every { observe() } returns flowOf(null)
                coEvery { get() } returns null
                coEvery { upsert(any()) } returns Unit
                coEvery { setSessionCookie(any()) } returns Unit
                coEvery { setBaseUrl(any()) } returns Unit
            }
        authStore = AuthStore(dao, TestScope(UnconfinedTestDispatcher()))
    }

    @AfterEach
    fun teardown() {
        server.shutdown()
    }

    @Test
    fun `rewrites placeholder host to live baseUrl host port and scheme`() {
        // baseUrl points at the mock server; outbound request targets the
        // placeholder sentinel — interceptor should rewrite to the mock.
        authStore.setBaseUrl(server.url("/").toString())
        server.enqueue(MockResponse().setResponseCode(200))
        val client = OkHttpClient.Builder().addInterceptor(BaseUrlInterceptor(authStore)).build()

        client.newCall(
            Request.Builder().url("http://placeholder.invalid/api/test").build(),
        ).execute()

        val recorded = server.takeRequest()
        assertEquals("/api/test", recorded.path)
        // MockWebServer's recorded Host header is the rewritten host:port
        assertEquals("${server.hostName}:${server.port}", recorded.getHeader("Host"))
    }

    @Test
    fun `passes external absolute URL through unchanged`() {
        // Lidarr-surfaced artwork hosts (artwork.musicbrainz.org,
        // coverartarchive.org) are absolute external URLs. The
        // interceptor must NOT rewrite them to the Minstrel base —
        // doing so produces 404s the user saw as missing Discover
        // suggestion covers.
        authStore.setBaseUrl(server.url("/").toString())
        var seen: HttpUrl? = null
        val client =
            OkHttpClient.Builder()
                .addInterceptor(BaseUrlInterceptor(authStore))
                .addInterceptor { chain ->
                    seen = chain.request().url
                    Response.Builder()
                        .request(chain.request())
                        .protocol(Protocol.HTTP_1_1)
                        .code(200)
                        .message("OK")
                        .body("".toResponseBody())
                        .build()
                }
                .build()

        client.newCall(
            Request.Builder()
                .url("https://artwork.musicbrainz.org/release-group/abc/front-250.jpg")
                .build(),
        ).execute()

        assertEquals("artwork.musicbrainz.org", seen?.host)
        assertEquals("https", seen?.scheme)
        assertEquals("/release-group/abc/front-250.jpg", seen?.encodedPath)
    }

    @Test
    fun `falls through when stored baseUrl is unparseable`() {
        // Empty / malformed baseUrl can happen between cold start and
        // first AuthStore load; the interceptor should leave the
        // request alone rather than throw.
        authStore.setBaseUrl("not a url")
        var seen: HttpUrl? = null
        val client =
            OkHttpClient.Builder()
                .addInterceptor(BaseUrlInterceptor(authStore))
                .addInterceptor { chain ->
                    seen = chain.request().url
                    Response.Builder()
                        .request(chain.request())
                        .protocol(Protocol.HTTP_1_1)
                        .code(200)
                        .message("OK")
                        .body("".toResponseBody())
                        .build()
                }
                .build()

        client.newCall(
            Request.Builder().url("http://placeholder.invalid/api/test").build(),
        ).execute()

        assertEquals("placeholder.invalid", seen?.host)
    }
}
