/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.ui.components

import com.runicgateway.app.data.api.dto.BrandDto
import com.runicgateway.app.data.appearance.SiteAppearance
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test

/**
 * §5.6's one testable rule: **an empty slot resolves to nothing.** The drawing
 * itself is out of reach here — the app carries no Robolectric, so a composable
 * body cannot run in a JVM test and phase 4's layout is AC-5's job — but the
 * decision of whether to draw at all is pure, and it is the decision that keeps
 * an unbranded instance laying out as it did before M12.
 *
 * The resolver is faked as the absolute-URL join the real one performs
 * (`AppViewModel.resolveAsset`, unchanged by this phase), so these assert
 * [brandAssetUrl]'s own contract rather than re-testing the network layer.
 */
class BrandAssetsTest {

    private val resolve: (String?) -> String? = { path ->
        when {
            path.isNullOrBlank() -> null
            path.startsWith("http") -> path
            else -> "https://shard.example${if (path.startsWith("/")) "" else "/"}$path"
        }
    }

    // --- the empty slot: every shape "not set" arrives in ------------------

    @Test
    fun `a null slot resolves to nothing`() {
        assertNull(brandAssetUrl(null, resolve))
    }

    @Test
    fun `an empty slot resolves to nothing`() {
        // The server publishes "" for an asset that was never uploaded, and BrandDto
        // defaults to it — this is the case that carries the untouched instance.
        assertNull(brandAssetUrl("", resolve))
    }

    @Test
    fun `a whitespace-only slot resolves to nothing`() {
        assertNull(brandAssetUrl("   ", resolve))
    }

    @Test
    fun `the shipped brand has neither a logo nor a hero`() {
        // AC-1 for phase 4: nothing about a default BrandDto puts an image on screen.
        val brand = BrandDto()
        assertNull(brandAssetUrl(brand.logo, resolve))
        assertNull(brandAssetUrl(brand.hero, resolve))
    }

    @Test
    fun `a failed settings load leaves no brand to draw`() {
        // SiteAppearance.NONE is what a dead backend produces (§2). It has no brand
        // at all, so both slots are absent rather than empty.
        val brand: BrandDto? = SiteAppearance.NONE.brand
        assertNull(brand)
        assertNull(brandAssetUrl(brand?.logo, resolve))
        assertNull(brandAssetUrl(brand?.hero, resolve))
    }

    // --- the filled slot ---------------------------------------------------

    @Test
    fun `a site-relative upload resolves against the shard's base`() {
        assertEquals(
            "https://shard.example/uploads/brand/logo.png",
            brandAssetUrl("/uploads/brand/logo.png", resolve),
        )
    }

    @Test
    fun `an absolute URL passes through`() {
        // BRAND_LOGO may be set to an off-site URL; the resolver leaves those alone.
        assertEquals(
            "https://cdn.example/logo.svg",
            brandAssetUrl("https://cdn.example/logo.svg", resolve),
        )
    }

    // --- the second blank check -------------------------------------------

    @Test
    fun `a resolver that returns nothing resolves to nothing`() {
        // No base URL configured yet: the real resolver hands the path back or gives
        // up. Either way the slot must not become an image request.
        assertNull(brandAssetUrl("/uploads/brand/logo.png") { null })
    }

    @Test
    fun `a resolver that returns blank resolves to nothing`() {
        // Why the blank check is on both sides of the resolver, not just the input.
        assertNull(brandAssetUrl("/uploads/brand/logo.png") { "" })
        assertNull(brandAssetUrl("/uploads/brand/logo.png") { "  " })
    }
}
