package ch.threema.app.onprem

import ch.threema.base.ThreemaException
import ch.threema.domain.onprem.OnPremConfig
import ch.threema.domain.onprem.OnPremConfigAvatar
import ch.threema.domain.onprem.OnPremConfigBlob
import ch.threema.domain.onprem.OnPremConfigChat
import ch.threema.domain.onprem.OnPremConfigDirectory
import ch.threema.domain.onprem.OnPremConfigFetcher
import ch.threema.domain.onprem.OnPremConfigMaps
import ch.threema.domain.onprem.OnPremConfigMediator
import ch.threema.domain.onprem.OnPremConfigSafe
import ch.threema.domain.onprem.OnPremConfigWeb
import ch.threema.domain.onprem.OnPremConfigWork
import ch.threema.domain.protocol.urls.BlobUrl
import ch.threema.domain.protocol.urls.MapPoiAroundUrl
import ch.threema.domain.protocol.urls.MapPoiNamesUrl
import ch.threema.domain.protocol.urls.MediatorUrl
import io.mockk.every
import io.mockk.mockk
import java.time.Instant
import kotlin.test.BeforeTest
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNull

class OnPremServerAddressProviderTest {

    private lateinit var fetcherProvider: OnPremServerAddressProvider.FetcherProvider
    private lateinit var onPremConfigFetcher: OnPremConfigFetcher
    private lateinit var serverAddressProvider: OnPremServerAddressProvider

    private val onPremConfig = OnPremConfig(
        validUntil = Instant.ofEpochMilli(999L),
        license = mockk(),
        domains = null,
        chat = OnPremConfigChat(
            hostname = "chat.threemaonprem.initrode.com",
            ports = intArrayOf(5222, 443),
            publicKey = byteArrayOf(0x1, 0x2, 0x3),
        ),
        directory = OnPremConfigDirectory(
            url = "https://dir.threemaonprem.initrode.com/directory",
        ),
        blob = OnPremConfigBlob(
            uploadUrl = "https://blob.threemaonprem.initrode.com/blob/upload",
            downloadUrl = BlobUrl("https://blob-{blobIdPrefix}.threemaonprem.initrode.com/blob/{blobId}"),
            doneUrl = BlobUrl("https://blob-{blobIdPrefix}.threemaonprem.initrode.com/blob/{blobId}/done"),
        ),
        work = OnPremConfigWork(
            url = "https://work.threemaonprem.initrode.com/",
        ),
        avatar = OnPremConfigAvatar(
            url = "https://avatar.threemaonprem.initrode.com/",
        ),
        safe = OnPremConfigSafe(
            url = "https://safe.threemaonprem.initrode.com/",
        ),
        web = OnPremConfigWeb(
            url = "https://web.threemaonprem.initrode.com/",
            overrideSaltyRtcHost = null,
            overrideSaltyRtcPort = 0,
        ),
        mediator = OnPremConfigMediator(
            url = MediatorUrl("https://mediator.threemaonprem.initrode.com/"),
            blob = OnPremConfigBlob(
                uploadUrl = "https://mediator.threemaonprem.initrode.com/blob/upload",
                downloadUrl = BlobUrl("https://mediator.threemaonprem.initrode.com/blob/{blobId}"),
                doneUrl = BlobUrl("https://mediator.threemaonprem.initrode.com/blob/{blobId}/done"),
            ),
        ),
        maps = OnPremConfigMaps(
            styleUrl = "https://map.initrode.com/styles/threema/style.json",
            poiNamesUrl = MapPoiNamesUrl("https://poi.initrode.com/names/{latitude}/{longitude}/{query}/"),
            poiAroundUrl = MapPoiAroundUrl("https://poi.initrode.com/around/{latitude}/{longitude}/{radius}/"),
        ),
    )

    @BeforeTest
    fun setUp() {
        onPremConfigFetcher = mockk {
            every { getOrFetch() } returns onPremConfig
        }
        fetcherProvider = OnPremServerAddressProvider.FetcherProvider { onPremConfigFetcher }
        serverAddressProvider = OnPremServerAddressProvider(fetcherProvider)
    }

    @Test
    fun `test chat server name prefix`() {
        assertEquals("", serverAddressProvider.getChatServerNamePrefix())
    }

    @Test
    fun `test chat server name suffix`() {
        assertEquals("chat.threemaonprem.initrode.com", serverAddressProvider.getChatServerNameSuffix())
    }

    @Test
    fun `test chat server ports`() {
        assertContentEquals(intArrayOf(5222, 443), serverAddressProvider.getChatServerPorts())
    }

    @Test
    fun `test chat server use server groups`() {
        assertFalse(serverAddressProvider.getChatServerUseServerGroups())
    }

    @Test
    fun `test chat server public key`() {
        assertContentEquals(byteArrayOf(0x1, 0x2, 0x3), serverAddressProvider.getChatServerPublicKey())
        assertContentEquals(byteArrayOf(0x1, 0x2, 0x3), serverAddressProvider.getChatServerPublicKeyAlt())
    }

    @Test
    fun `test directory server url`() {
        assertEquals(
            "https://dir.threemaonprem.initrode.com/directory",
            serverAddressProvider.getDirectoryServerUrl(),
        )
    }

    @Test
    fun `test work server url`() {
        assertEquals(
            "https://work.threemaonprem.initrode.com/",
            serverAddressProvider.getWorkServerUrlLegacy(),
        )
    }

    @Test
    fun `test blob server download url`() {
        assertEquals(
            BlobUrl("https://blob-{blobIdPrefix}.threemaonprem.initrode.com/blob/{blobId}"),
            serverAddressProvider.getBlobServerDownloadUrl(),
        )
    }

    @Test
    fun `test blob server upload url`() {
        assertEquals(
            "https://blob.threemaonprem.initrode.com/blob/upload",
            serverAddressProvider.getBlobServerUploadUrl(),
        )
    }

    @Test
    fun `test blob server done url`() {
        assertEquals(
            BlobUrl("https://blob-{blobIdPrefix}.threemaonprem.initrode.com/blob/{blobId}/done"),
            serverAddressProvider.getBlobServerDoneUrl(),
        )
    }

    @Test
    fun `test blob mirror server download url`() {
        assertEquals(
            BlobUrl("https://mediator.threemaonprem.initrode.com/blob/{blobId}"),
            serverAddressProvider.getBlobMirrorServerDownloadUrl(multiDevicePropertyProvider = mockk()),
        )
    }

    @Test
    fun `test blob mirror server upload url`() {
        assertEquals(
            "https://mediator.threemaonprem.initrode.com/blob/upload",
            serverAddressProvider.getBlobMirrorServerUploadUrl(multiDevicePropertyProvider = mockk()),
        )
    }

    @Test
    fun `test blob mirror server done url`() {
        assertEquals(
            BlobUrl("https://mediator.threemaonprem.initrode.com/blob/{blobId}/done"),
            serverAddressProvider.getBlobMirrorServerDoneUrl(multiDevicePropertyProvider = mockk()),
        )
    }

    @Test
    fun `test avatar server url`() {
        assertEquals(
            "https://avatar.threemaonprem.initrode.com/",
            serverAddressProvider.getAvatarServerUrl(),
        )
    }

    @Test
    fun `test safe server url`() {
        assertEquals(
            "https://safe.threemaonprem.initrode.com/",
            serverAddressProvider.getSafeServerUrl(),
        )
    }

    @Test
    fun `test web server url`() {
        assertEquals(
            "https://web.threemaonprem.initrode.com/",
            serverAddressProvider.getWebServerUrl(),
        )
    }

    @Test
    fun `test web override salty rtc host`() {
        assertNull(serverAddressProvider.getWebOverrideSaltyRtcHost())
    }

    @Test
    fun `test web override salty rtc port`() {
        assertEquals(0, serverAddressProvider.getWebOverrideSaltyRtcPort())
    }

    @Test
    fun `test threema push public key`() {
        assertNull(serverAddressProvider.getThreemaPushPublicKey())
    }

    @Test
    fun `test mediator url`() {
        assertEquals(
            MediatorUrl("https://mediator.threemaonprem.initrode.com/"),
            serverAddressProvider.getMediatorUrl(),
        )
    }

    @Test
    fun `test mediator url when no mediator config available`() {
        onPremConfigFetcher = mockk {
            every { getOrFetch() } returns onPremConfig.copy(
                mediator = null,
            )
        }

        assertFailsWith<ThreemaException> {
            serverAddressProvider.getMediatorUrl()
        }
    }

    @Test
    fun `test no app rating url available`() {
        assertFailsWith<ThreemaException> {
            serverAddressProvider.getAppRatingUrl()
        }
    }

    @Test
    fun `test maps style url when maps config available`() {
        assertEquals(
            "https://map.initrode.com/styles/threema/style.json",
            serverAddressProvider.getMapStyleUrl(),
        )
    }

    @Test
    @Ignore("disabled until fallback is removed") // TODO(ANDR-3805)
    fun `test maps style url when maps config not available`() {
        onPremConfigFetcher = mockk {
            every { getOrFetch() } returns onPremConfig.copy(
                maps = null,
            )
        }

        assertNull(serverAddressProvider.getMapStyleUrl())
    }

    @Test
    fun `test maps POI names url when maps config available`() {
        assertEquals(
            MapPoiNamesUrl("https://poi.initrode.com/names/{latitude}/{longitude}/{query}/"),
            serverAddressProvider.getMapPoiNamesUrl(),
        )
    }

    @Test
    @Ignore("disabled until fallback is removed") // TODO(ANDR-3805)
    fun `test maps POI names url when maps config not available`() {
        onPremConfigFetcher = mockk {
            every { getOrFetch() } returns onPremConfig.copy(
                maps = null,
            )
        }

        assertNull(serverAddressProvider.getMapPoiNamesUrl())
    }

    @Test
    fun `test maps POI around url when maps config available`() {
        assertEquals(
            MapPoiAroundUrl("https://poi.initrode.com/around/{latitude}/{longitude}/{radius}/"),
            serverAddressProvider.getMapPoiAroundUrl(),
        )
    }

    @Test
    @Ignore("disabled until fallback is removed") // TODO(ANDR-3805)
    fun `test maps POI around url when maps config not available`() {
        onPremConfigFetcher = mockk {
            every { getOrFetch() } returns onPremConfig.copy(
                maps = null,
            )
        }

        assertNull(serverAddressProvider.getMapPoiAroundUrl())
    }
}
