package com.fsck.k9.ui.messageview

import android.graphics.Color
import android.text.Spannable
import android.text.style.ForegroundColorSpan
import androidx.core.text.getSpans
import assertk.assertThat
import assertk.assertions.containsExactly
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import com.fsck.k9.helper.ContactNameProvider
import com.fsck.k9.mail.Address
import net.thunderbird.account.fake.FakeAccountData.ACCOUNT_ID_RAW
import net.thunderbird.core.android.account.Identity
import net.thunderbird.core.android.account.LegacyAccountDto
import net.thunderbird.core.android.testing.RobolectricTest
import org.junit.Test

private const val IDENTITY_ADDRESS = "me@domain.example"
private const val ME_TEXT = "me"

class MessageViewRecipientFormatterTest : RobolectricTest() {
    private val contactNameProvider = object : ContactNameProvider {
        override fun getNameForAddress(address: String): String? {
            return when (address) {
                "user1@domain.example" -> "Contact One"
                "spoof@domain.example" -> "contact@important.example"
                else -> null
            }
        }
    }

    private val account = LegacyAccountDto(ACCOUNT_ID_RAW).apply {
        identities += Identity(email = IDENTITY_ADDRESS)
    }

    @Test
    fun `single identity`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address(IDENTITY_ADDRESS, "irrelevant"), account)

        assertThat(displayName).isEqualTo(ME_TEXT)
    }

    @Test
    fun `multiple identities`() {
        val account = LegacyAccountDto(ACCOUNT_ID_RAW).apply {
            identities += Identity(
                description = "My identity",
                email = IDENTITY_ADDRESS,
            )
            identities += Identity(email = "another.one@domain.example")
        }
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address(IDENTITY_ADDRESS, "irrelevant"), account)

        assertThat(displayName).isEqualTo("My identity")
    }

    @Test
    fun `identity without a description`() {
        val account = LegacyAccountDto(ACCOUNT_ID_RAW).apply {
            identities += Identity(name = "My name", email = IDENTITY_ADDRESS)
            identities += Identity(email = "another.one@domain.example")
        }
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address(IDENTITY_ADDRESS, "irrelevant"), account)

        assertThat(displayName).isEqualTo("My name")
    }

    @Test
    fun `identity without a description and name`() {
        val account = LegacyAccountDto(ACCOUNT_ID_RAW).apply {
            identities += Identity(email = IDENTITY_ADDRESS)
            identities += Identity(email = "another.one@domain.example")
        }
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address(IDENTITY_ADDRESS, "irrelevant"), account)

        assertThat(displayName).isEqualTo(IDENTITY_ADDRESS)
    }

    @Test
    fun `email address without display name`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address("alice@domain.example"), account)

        assertThat(displayName).isEqualTo("alice@domain.example")
    }

    @Test
    fun `email address with display name`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address("alice@domain.example", "Alice"), account)

        assertThat(displayName).isEqualTo("Alice")
    }

    @Test
    fun `don't look up contact when showContactNames = false`() {
        val recipientFormatter = createRecipientFormatter(showContactNames = false)

        val displayName = recipientFormatter.getDisplayName(Address("user1@domain.example", "User 1"), account)

        assertThat(displayName).isEqualTo("User 1")
    }

    @Test
    fun `contact lookup`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address("user1@domain.example"), account)

        assertThat(displayName).isEqualTo("Contact One")
    }

    @Test
    fun `contact lookup despite display name`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address("user1@domain.example", "User 1"), account)

        assertThat(displayName).isEqualTo("Contact One")
    }

    @Test
    fun `colored contact name`() {
        val recipientFormatter = createRecipientFormatter(contactNameColor = Color.RED)

        val displayName = recipientFormatter.getDisplayName(Address("user1@domain.example"), account)

        assertThat(displayName.toString()).isEqualTo("Contact One")
        assertThat(displayName).isInstanceOf<Spannable>()
        val spans = (displayName as Spannable).getSpans<ForegroundColorSpan>(0, displayName.length)
        assertThat(spans.map { it.foregroundColor }).containsExactly(Color.RED)
    }

    @Test
    fun `email address with display name but not showing correspondent names`() {
        val recipientFormatter = createRecipientFormatter(showCorrespondentNames = false)

        val displayName = recipientFormatter.getDisplayName(Address("alice@domain.example", "Alice"), account)

        assertThat(displayName).isEqualTo("alice@domain.example")
    }

    @Test
    fun `do not show display name that looks like an email address`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(
            Address("mallory@domain.example", "potus@whitehouse.gov"),
            account,
        )

        assertThat(displayName).isEqualTo("mallory@domain.example")
    }

    @Test
    fun `do show display name that contains an @ preceded by an opening parenthesis`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(
            Address("gitlab@gitlab.example", "username (@username)"),
            account,
        )

        assertThat(displayName).isEqualTo("username (@username)")
    }

    @Test
    fun `do show display name that starts with an @`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address("address@domain.example", "@username"), account)

        assertThat(displayName).isEqualTo("@username")
    }

    @Test
    fun `spoof prevention doesn't apply to contact names`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(
            Address("spoof@domain.example", "contact@important.example"),
            account,
        )

        assertThat(displayName).isEqualTo("contact@important.example")
    }

    @Test
    fun `display name matches me text`() {
        val recipientFormatter = createRecipientFormatter()

        val displayName = recipientFormatter.getDisplayName(Address("someone_named_me@domain.example", "ME"), account)

        assertThat(displayName).isEqualTo("someone_named_me@domain.example")
    }

    private fun createRecipientFormatter(
        showCorrespondentNames: Boolean = true,
        showContactNames: Boolean = true,
        contactNameColor: Int? = null,
    ): RealMessageViewRecipientFormatter {
        return RealMessageViewRecipientFormatter(
            contactNameProvider = contactNameProvider,
            showCorrespondentNames = showCorrespondentNames,
            showContactNames = showContactNames,
            contactNameColor = contactNameColor,
            meText = ME_TEXT,
        )
    }
}
