package jp.juggler.subwaytooter.util

import android.content.Context
import android.content.Intent
import androidx.annotation.StringRes
import jp.juggler.subwaytooter.ActText
import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.PrefB
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.notEmpty
import java.util.*

object TootTextEncoder {

    private fun StringBuilder.addAfterLine(text: CharSequence) {
        if (isNotEmpty() && this[length - 1] != '\n') {
            append('\n')
        }
        append(text)
    }

    private fun addHeader(
        context: Context,
        sb: StringBuilder,
        @StringRes keyStrId: Int,
        value: Any?
    ) {
        if (sb.isNotEmpty() && sb[sb.length - 1] != '\n') {
            sb.append('\n')
        }
        sb.addAfterLine(context.getString(keyStrId))
        sb.append(": ")
        sb.append(value?.toString() ?: "(null)")
    }

    fun encodeStatus(
        intent: Intent,
        context: Context,
        accessInfo: SavedAccount,
        status: TootStatus
    ) {
        val sb = StringBuilder()

        addHeader(context, sb, R.string.send_header_url, status.url)

        addHeader(
            context,
            sb,
            R.string.send_header_date,
            TootStatus.formatTime(context, status.time_created_at, false)
        )

        addHeader(
            context,
            sb,
            R.string.send_header_from_acct,
            accessInfo.getFullAcct(status.account)
        )

        val sv: String = status.spoiler_text
        if (sv.isNotEmpty()) {
            addHeader(context, sb, R.string.send_header_content_warning, sv)
        }

        sb.addAfterLine("\n")

        intent.putExtra(ActText.EXTRA_CONTENT_START, sb.length)
        sb.append(
            DecodeOptions(
                context,
                accessInfo,
                mentions = status.mentions,
                mentionDefaultHostDomain = status.account
            ).decodeHTML(status.content)
        )

        encodePolls(sb, context, status)

        intent.putExtra(ActText.EXTRA_CONTENT_END, sb.length)

        dumpAttachment(sb, status.media_attachments)

        sb.addAfterLine(String.format(Locale.JAPAN, "Status-Source: %s", status.json.toString(2)))
        sb.addAfterLine("")
        intent.putExtra(ActText.EXTRA_TEXT, sb.toString())
    }

    fun encodeStatusForTranslate(
        context: Context,
        accessInfo: SavedAccount,
        status: TootStatus
    ): String {
        val sb = StringBuilder()

        status.spoiler_text.notEmpty()?.let {
            sb.append(it).append("\n\n")
        }

        sb.append(
            DecodeOptions(
                context,
                accessInfo,
                mentions = status.mentions,
                mentionDefaultHostDomain = status.account
            ).decodeHTML(status.content)
        )

        encodePolls(sb, context, status)

        return sb.toString()
    }

    private fun dumpAttachment(sb: StringBuilder, src: ArrayList<TootAttachmentLike>?) {
        if (src == null) return
        var i = 0
        for (ma in src) {
            ++i
            if (ma is TootAttachment) {
                sb.addAfterLine("\n")
                sb.addAfterLine(String.format(Locale.JAPAN, "Media-%d-Url: %s", i, ma.url))
                sb.addAfterLine(
                    String.format(Locale.JAPAN, "Media-%d-Remote-Url: %s", i, ma.remote_url)
                )
                sb.addAfterLine(
                    String.format(Locale.JAPAN, "Media-%d-Preview-Url: %s", i, ma.preview_url)
                )
                sb.addAfterLine(
                    String.format(Locale.JAPAN, "Media-%d-Text-Url: %s", i, ma.text_url)
                )
            } else if (ma is TootAttachmentMSP) {
                sb.addAfterLine("\n")
                sb.addAfterLine(
                    String.format(Locale.JAPAN, "Media-%d-Preview-Url: %s", i, ma.preview_url)
                )
            }
        }
    }

    private fun encodePolls(sb: StringBuilder, context: Context, status: TootStatus) {
        val enquete = status.enquete ?: return
        val items = enquete.items ?: return
        val now = System.currentTimeMillis()

        val canVote = when (enquete.pollType) {

            // friends.nico の場合は本文に投票の選択肢が含まれるので
            // アプリ側での文字列化は不要
            TootPollsType.FriendsNico -> return

            // MastodonとMisskeyは投票の選択肢が本文に含まれないので
            // アプリ側で文字列化する
            TootPollsType.Mastodon, TootPollsType.Notestock -> when {
                enquete.expired -> false
                now >= enquete.expired_at -> false
                enquete.ownVoted -> false
                else -> true
            }

            TootPollsType.Misskey -> enquete.ownVoted
        }

        sb.addAfterLine("\n")

        items.forEach { choice ->
            encodePollChoice(sb, context, enquete, canVote, choice)
        }

        when (enquete.pollType) {
            TootPollsType.Mastodon -> encodePollFooterMastodon(sb, context, enquete)

            else -> {
            }
        }
    }

    private fun encodePollChoice(
        sb: StringBuilder,
        context: Context,
        enquete: TootPolls,
        canVote: Boolean,
        item: TootPollsChoice
    ) {

        val text = when (enquete.pollType) {
            TootPollsType.Misskey -> {
                val sb2 = StringBuilder().append(item.decoded_text)
                if (enquete.ownVoted) {
                    sb2.append(" / ")
                    sb2.append(context.getString(R.string.vote_count_text, item.votes))
                    if (item.isVoted) sb2.append(' ').append(0x2713.toChar())
                }
                sb2
            }

            TootPollsType.FriendsNico -> {
                item.decoded_text
            }

            TootPollsType.Mastodon, TootPollsType.Notestock -> if (canVote) {
                item.decoded_text
            } else {
                val sb2 = StringBuilder().append(item.decoded_text)
                if (!canVote) {
                    sb2.append(" / ")
                    sb2.append(
                        when (val v = item.votes) {
                            null -> context.getString(R.string.vote_count_unavailable)
                            else -> context.getString(R.string.vote_count_text, v)
                        }
                    )
                }
                sb2
            }
        }

        sb.addAfterLine(text)
    }

    private fun encodePollFooterMastodon(
        sb: StringBuilder,
        context: Context,
        enquete: TootPolls
    ) {
        val line = StringBuilder()

        val votes_count = enquete.votes_count ?: 0
        when {
            votes_count == 1 -> line.append(context.getString(R.string.vote_1))
            votes_count > 1 -> line.append(context.getString(R.string.vote_2, votes_count))
        }

        when (val t = enquete.expired_at) {

            Long.MAX_VALUE -> {
            }

            else -> {
                if (line.isNotEmpty()) line.append(" ")
                line.append(
                    context.getString(
                        R.string.vote_expire_at,
                        TootStatus.formatTime(context, t, false)
                    )
                )
            }
        }
        sb.addAfterLine(line)
    }

    fun encodeAccount(
        intent: Intent,
        context: Context,
        accessInfo: SavedAccount,
        who: TootAccount
    ) {
        val sb = StringBuilder()

        intent.putExtra(ActText.EXTRA_CONTENT_START, sb.length)
        sb.append(who.display_name)
        sb.append("\n")
        sb.append("@")
        sb.append(accessInfo.getFullAcct(who))
        sb.append("\n")

        intent.putExtra(ActText.EXTRA_CONTENT_START, sb.length)
        sb.append(who.url)
        intent.putExtra(ActText.EXTRA_CONTENT_END, sb.length)

        sb.addAfterLine("\n")

        sb.append(
            DecodeOptions(
                context,
                accessInfo,
                mentionDefaultHostDomain = who
            ).decodeHTML(who.note)
        )

        sb.addAfterLine("\n")

        addHeader(context, sb, R.string.send_header_account_name, who.display_name)
        addHeader(context, sb, R.string.send_header_account_acct, accessInfo.getFullAcct(who))
        addHeader(context, sb, R.string.send_header_account_url, who.url)

        addHeader(context, sb, R.string.send_header_account_image_avatar, who.avatar)
        addHeader(
            context,
            sb,
            R.string.send_header_account_image_avatar_static,
            who.avatar_static
        )
        addHeader(context, sb, R.string.send_header_account_image_header, who.header)
        addHeader(
            context,
            sb,
            R.string.send_header_account_image_header_static,
            who.header_static
        )

        addHeader(context, sb, R.string.send_header_account_created_at, who.created_at)
        addHeader(context, sb, R.string.send_header_account_statuses_count, who.statuses_count)

        if (!PrefB.bpHideFollowCount(App1.getAppState(context).pref)) {
            addHeader(
                context,
                sb,
                R.string.send_header_account_followers_count,
                who.followers_count
            )
            addHeader(
                context,
                sb,
                R.string.send_header_account_following_count,
                who.following_count
            )
        }
        addHeader(context, sb, R.string.send_header_account_locked, who.locked)

        sb.addAfterLine(String.format(Locale.JAPAN, "Account-Source: %s", who.json.toString(2)))
        sb.addAfterLine("")

        intent.putExtra(ActText.EXTRA_TEXT, sb.toString())
    }
}
