package jp.juggler.subwaytooter

import android.app.Dialog
import android.graphics.Color
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.View
import android.widget.*
import jp.juggler.subwaytooter.emoji.EmojiMap
import jp.juggler.subwaytooter.action.*
import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.dialog.DlgTextInput
import jp.juggler.subwaytooter.span.EmojiImageSpan
import jp.juggler.subwaytooter.span.LinkInfo
import jp.juggler.subwaytooter.span.MyClickableSpan
import jp.juggler.subwaytooter.span.createSpan
import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.table.UserRelation
import jp.juggler.subwaytooter.util.*
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
import jp.juggler.subwaytooter.view.MyNetworkImageView
import jp.juggler.subwaytooter.view.MyTextView
import jp.juggler.util.*
import org.jetbrains.anko.textColor

internal class ViewHolderHeaderProfile(
    activity: ActMain,
    viewRoot: View,
) : ViewHolderHeaderBase(activity, viewRoot), View.OnClickListener, View.OnLongClickListener {

    companion object {
        private fun SpannableStringBuilder.appendSpan(text: String, span: Any) {
            val start = length
            append(text)
            setSpan(
                span,
                start,
                length,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
    }

    private val ivBackground: MyNetworkImageView
    private val tvCreated: TextView
    private val tvLastStatusAt: TextView
    private val tvFeaturedTags: TextView

    private val ivAvatar: MyNetworkImageView
    private val tvDisplayName: TextView
    private val tvAcct: TextView
    private val btnFollowing: Button
    private val btnFollowers: Button
    private val btnStatusCount: Button
    private val tvNote: TextView
    private val tvMisskeyExtra: TextView

    private val btnFollow: ImageButton
    private val ivFollowedBy: ImageView
    private val llProfile: View
    private val tvRemoteProfileWarning: TextView
    private val nameInvalidator1: NetworkEmojiInvalidator
    private val noteInvalidator: NetworkEmojiInvalidator
    private val llFields: LinearLayout

    private var whoRef: TootAccountRef? = null

    private var movedRef: TootAccountRef? = null

    private val llMoved: View
    private val tvMoved: TextView
    private val ivMoved: MyNetworkImageView
    private val tvMovedName: TextView
    private val tvMovedAcct: TextView
    private val btnMoved: ImageButton
    private val ivMovedBy: ImageView
    private val movedCaptionInvalidator: NetworkEmojiInvalidator
    private val movedNameInvalidator: NetworkEmojiInvalidator
    private val density: Float
    private val btnMore: ImageButton

    private val tvPersonalNotes: TextView
    private val btnPersonalNotesEdit: ImageButton

    private var contentColor = 0
    private var relation: UserRelation? = null

    init {
        ivBackground = viewRoot.findViewById(R.id.ivBackground)
        llProfile = viewRoot.findViewById(R.id.llProfile)
        tvCreated = viewRoot.findViewById(R.id.tvCreated)
        tvLastStatusAt = viewRoot.findViewById(R.id.tvLastStatusAt)
        tvFeaturedTags = viewRoot.findViewById(R.id.tvFeaturedTags)
        ivAvatar = viewRoot.findViewById(R.id.ivAvatar)
        tvDisplayName = viewRoot.findViewById(R.id.tvDisplayName)
        tvAcct = viewRoot.findViewById(R.id.tvAcct)
        btnFollowing = viewRoot.findViewById(R.id.btnFollowing)
        btnFollowers = viewRoot.findViewById(R.id.btnFollowers)
        btnStatusCount = viewRoot.findViewById(R.id.btnStatusCount)
        tvNote = viewRoot.findViewById(R.id.tvNote)
        tvMisskeyExtra = viewRoot.findViewById(R.id.tvMisskeyExtra)
        btnMore = viewRoot.findViewById(R.id.btnMore)
        btnFollow = viewRoot.findViewById(R.id.btnFollow)
        ivFollowedBy = viewRoot.findViewById(R.id.ivFollowedBy)
        tvRemoteProfileWarning = viewRoot.findViewById(R.id.tvRemoteProfileWarning)

        llMoved = viewRoot.findViewById(R.id.llMoved)
        tvMoved = viewRoot.findViewById(R.id.tvMoved)
        ivMoved = viewRoot.findViewById(R.id.ivMoved)
        tvMovedName = viewRoot.findViewById(R.id.tvMovedName)
        tvMovedAcct = viewRoot.findViewById(R.id.tvMovedAcct)
        btnMoved = viewRoot.findViewById(R.id.btnMoved)
        ivMovedBy = viewRoot.findViewById(R.id.ivMovedBy)
        llFields = viewRoot.findViewById(R.id.llFields)

        tvPersonalNotes = viewRoot.findViewById(R.id.tvPersonalNotes)
        btnPersonalNotesEdit = viewRoot.findViewById(R.id.btnPersonalNotesEdit)

        density = tvDisplayName.resources.displayMetrics.density

        for (v in arrayOf(
            ivBackground,
            btnFollowing,
            btnFollowers,
            btnStatusCount,
            btnMore,
            btnFollow,
            tvRemoteProfileWarning,
            btnPersonalNotesEdit,

            btnMoved,
            llMoved,
            btnPersonalNotesEdit
        )) {
            v.setOnClickListener(this)
        }

        btnMoved.setOnLongClickListener(this)
        btnFollow.setOnLongClickListener(this)

        tvNote.movementMethod = MyLinkMovementMethod

        nameInvalidator1 = NetworkEmojiInvalidator(activity.handler, tvDisplayName)
        noteInvalidator = NetworkEmojiInvalidator(activity.handler, tvNote)
        movedCaptionInvalidator = NetworkEmojiInvalidator(activity.handler, tvMoved)
        movedNameInvalidator = NetworkEmojiInvalidator(activity.handler, tvMovedName)

        ivBackground.measureProfileBg = true
    }

    override fun getAccount(): TootAccountRef? = whoRef

    override fun onViewRecycled() {
    }

    //	fun updateRelativeTime() {
    //		val who = whoRef?.get()
    //		if(who != null) {
    //			tvCreated.text = TootStatus.formatTime(tvCreated.context, who.time_created_at, true)
    //		}
    //	}

    override fun bindData(column: Column) {
        super.bindData(column)

        bindFonts()
        bindColors()

        llMoved.visibility = View.GONE
        tvMoved.visibility = View.GONE
        llFields.visibility = View.GONE
        llFields.removeAllViews()

        val whoRef = column.whoAccount
        this.whoRef = whoRef
        when (val who = whoRef?.get()) {
            null -> bindAccountNull()
            else -> bindAccount(who, whoRef)
        }
    }

    // カラム設定から戻った際に呼ばれる
    override fun showColor() {
        llProfile.setBackgroundColor(
            when (val c = column.columnBgColor) {
                0 -> activity.attrColor(R.attr.colorProfileBackgroundMask)
                else -> -0x40000000 or (0x00ffffff and c)
            }
        )
    }

    // bind時に呼ばれる
    private fun bindColors() {
        val contentColor = column.getContentColor()
        this.contentColor = contentColor

        tvPersonalNotes.textColor = contentColor
        tvMoved.textColor = contentColor
        tvMovedName.textColor = contentColor
        tvDisplayName.textColor = contentColor
        tvNote.textColor = contentColor
        tvRemoteProfileWarning.textColor = contentColor
        btnStatusCount.textColor = contentColor
        btnFollowing.textColor = contentColor
        btnFollowers.textColor = contentColor
        tvFeaturedTags.textColor = contentColor

        setIconDrawableId(
            activity,
            btnMore,
            R.drawable.ic_more,
            color = contentColor,
            alphaMultiplier = Styler.boostAlpha
        )

        setIconDrawableId(
            activity,
            btnPersonalNotesEdit,
            R.drawable.ic_edit,
            color = contentColor,
            alphaMultiplier = Styler.boostAlpha
        )

        val acctColor = column.getAcctColor()
        tvCreated.textColor = acctColor
        tvMovedAcct.textColor = acctColor
        tvLastStatusAt.textColor = acctColor

        showColor()
    }

    private fun bindFonts() {
        var f: Float

        f = activity.timelineFontSizeSp
        if (!f.isNaN()) {
            tvMovedName.textSize = f
            tvMoved.textSize = f
            tvPersonalNotes.textSize = f
            tvFeaturedTags.textSize = f
        }

        f = activity.acctFontSizeSp
        if (!f.isNaN()) {
            tvMovedAcct.textSize = f
            tvCreated.textSize = f
            tvLastStatusAt.textSize = f
        }

        val spacing = activity.timelineSpacing
        if (spacing != null) {
            tvMovedName.setLineSpacing(0f, spacing)
            tvMoved.setLineSpacing(0f, spacing)
        }
    }

    private fun bindAccountNull() {
        relation = null
        tvCreated.text = ""
        tvLastStatusAt.vg(false)
        tvFeaturedTags.vg(false)
        ivBackground.setImageDrawable(null)
        ivAvatar.setImageDrawable(null)

        tvAcct.text = "@"

        tvDisplayName.text = ""
        nameInvalidator1.register(null)

        tvNote.text = ""
        tvMisskeyExtra.text = ""
        noteInvalidator.register(null)

        btnStatusCount.text = activity.getString(R.string.statuses) + "\n" + "?"
        btnFollowing.text = activity.getString(R.string.following) + "\n" + "?"
        btnFollowers.text = activity.getString(R.string.followers) + "\n" + "?"

        btnFollow.setImageDrawable(null)
        tvRemoteProfileWarning.visibility = View.GONE
    }

    private fun bindAccount(who: TootAccount, whoRef: TootAccountRef) {

        // Misskeyの場合はNote中のUserエンティティと /api/users/show の情報量がかなり異なる
        val whoDetail = MisskeyAccountDetailMap.get(accessInfo, who.id)

        tvCreated.text =
            TootStatus.formatTime(tvCreated.context, (whoDetail ?: who).time_created_at, true)

        who.setAccountExtra(
            accessInfo,
            tvLastStatusAt,
            invalidator = null,
            fromProfileHeader = true
        )

        val featuredTagsText = formatFeaturedTags()
        tvFeaturedTags.vg(featuredTagsText != null)?.let {
            it.text = featuredTagsText!!
            it.movementMethod = MyLinkMovementMethod
        }

        ivBackground.setImageUrl(activity.pref, 0f, accessInfo.supplyBaseUrl(who.header_static))

        ivAvatar.setImageUrl(
            activity.pref,
            Styler.calcIconRound(ivAvatar.layoutParams),
            accessInfo.supplyBaseUrl(who.avatar_static),
            accessInfo.supplyBaseUrl(who.avatar)
        )

        val name = whoDetail?.decodeDisplayName(activity) ?: whoRef.decoded_display_name
        tvDisplayName.text = name
        nameInvalidator1.register(name)

        tvRemoteProfileWarning.vg(column.accessInfo.isRemoteUser(who))

        tvAcct.text = encodeAcctText(who, whoDetail)

        val note = whoRef.decoded_note
        tvNote.text = note
        noteInvalidator.register(note)

        tvMisskeyExtra.text = encodeMisskeyExtra(whoDetail)
        tvMisskeyExtra.vg(tvMisskeyExtra.text.isNotEmpty())

        btnStatusCount.text =
            "${activity.getString(R.string.statuses)}\n${
                whoDetail?.statuses_count ?: who.statuses_count
            }"

        val hideFollowCount = PrefB.bpHideFollowCount(activity.pref)

        var caption = activity.getString(R.string.following)
        btnFollowing.text = when {
            hideFollowCount -> caption
            else -> "${caption}\n${whoDetail?.following_count ?: who.following_count}"
        }

        caption = activity.getString(R.string.followers)
        btnFollowers.text = when {
            hideFollowCount -> caption
            else -> "${caption}\n${whoDetail?.followers_count ?: who.followers_count}"
        }

        val relation = UserRelation.load(accessInfo.db_id, who.id)
        this.relation = relation
        Styler.setFollowIcon(
            activity,
            btnFollow,
            ivFollowedBy,
            relation,
            who,
            contentColor,
            alphaMultiplier = Styler.boostAlpha
        )

        tvPersonalNotes.text = relation.note ?: ""

        showMoved(who, who.movedRef)

        (whoDetail?.fields ?: who.fields)?.notEmpty()?.let { showFields(who, it) }
    }

    private fun showMoved(who: TootAccount, movedRef: TootAccountRef?) {
        if (movedRef == null) return
        this.movedRef = movedRef
        val moved = movedRef.get()

        llMoved.visibility = View.VISIBLE
        tvMoved.visibility = View.VISIBLE

        val caption = who.decodeDisplayName(activity)
            .intoStringResource(activity, R.string.account_moved_to)

        tvMoved.text = caption
        movedCaptionInvalidator.register(caption)

        ivMoved.layoutParams.width = activity.avatarIconSize
        ivMoved.setImageUrl(
            activity.pref,
            Styler.calcIconRound(ivMoved.layoutParams),
            accessInfo.supplyBaseUrl(moved.avatar_static)
        )

        tvMovedName.text = movedRef.decoded_display_name
        movedNameInvalidator.register(movedRef.decoded_display_name)

        setAcct(tvMovedAcct, accessInfo, moved)

        val relation = UserRelation.load(accessInfo.db_id, moved.id)
        Styler.setFollowIcon(
            activity,
            btnMoved,
            ivMovedBy,
            relation,
            moved,
            contentColor,
            alphaMultiplier = Styler.boostAlpha
        )
    }

    override fun onClick(v: View) {

        when (v.id) {

            R.id.ivBackground, R.id.tvRemoteProfileWarning ->
                activity.openCustomTab(whoRef?.get()?.url)

            R.id.btnFollowing -> {
                column.profileTab = ProfileTab.Following
                activity.appState.saveColumnList()
                column.startLoading()
            }

            R.id.btnFollowers -> {
                column.profileTab = ProfileTab.Followers
                activity.appState.saveColumnList()
                column.startLoading()
            }

            R.id.btnStatusCount -> {
                column.profileTab = ProfileTab.Status
                activity.appState.saveColumnList()
                column.startLoading()
            }

            R.id.btnMore -> whoRef?.let { whoRef ->
                DlgContextMenu(activity, column, whoRef, null, null, null).show()
            }

            R.id.btnFollow -> whoRef?.let { whoRef ->
                DlgContextMenu(activity, column, whoRef, null, null, null).show()
            }

            R.id.btnMoved -> movedRef?.let { movedRef ->
                DlgContextMenu(activity, column, movedRef, null, null, null).show()
            }

            R.id.llMoved -> movedRef?.let { movedRef ->
                if (accessInfo.isPseudo) {
                    DlgContextMenu(activity, column, movedRef, null, null, null).show()
                } else {
                    activity.userProfileLocal(

                        activity.nextPosition(column),
                        accessInfo,
                        movedRef.get()
                    )
                }
            }

            R.id.btnPersonalNotesEdit -> whoRef?.let { whoRef ->
                val who = whoRef.get()
                val relation = this.relation
                val lastColumn = column
                DlgTextInput.show(
                    activity,
                    AcctColor.getStringWithNickname(activity, R.string.personal_notes_of, who.acct),
                    relation?.note ?: "",
                    allowEmpty = true,
                    callback = object : DlgTextInput.Callback {
                        override fun onEmptyError() {
                        }

                        override fun onOK(dialog: Dialog, text: String) {
                            launchMain {
                                activity.runApiTask(column.accessInfo) { client ->
                                    when {
                                        accessInfo.isPseudo ->
                                            TootApiResult("Personal notes is not supported on pseudo account.")
                                        accessInfo.isMisskey ->
                                            TootApiResult("Personal notes is not supported on Misskey account.")
                                        else ->
                                            client.request(
                                                "/api/v1/accounts/${who.id}/note",
                                                jsonObject {
                                                    put("comment", text)
                                                }.toPostRequestBuilder()
                                            )
                                    }
                                }?.let { result ->
                                    when (val error = result.error) {
                                        null -> {
                                            relation?.note = text
                                            dialog.dismissSafe()
                                            if (lastColumn == column) bindData(column)
                                        }
                                        else -> activity.showToast(true, error)
                                    }
                                }
                            }
                        }
                    }
                )
            }
        }
    }

    override fun onLongClick(v: View): Boolean {
        when (v.id) {

            R.id.btnFollow -> {
                activity.followFromAnotherAccount(

                    activity.nextPosition(column),
                    accessInfo,
                    whoRef?.get()
                )
                return true
            }

            R.id.btnMoved -> {
                activity.followFromAnotherAccount(

                    activity.nextPosition(column),
                    accessInfo,
                    movedRef?.get()
                )
                return true
            }
        }

        return false
    }

    private fun setAcct(tv: TextView, accessInfo: SavedAccount, who: TootAccount) {
        val ac = AcctColor.load(accessInfo, who)
        tv.text = when {
            AcctColor.hasNickname(ac) -> ac.nickname
            PrefB.bpShortAcctLocalUser(App1.pref) -> "@${who.acct.pretty}"
            else -> "@${ac.nickname}"
        }

        tv.textColor = ac.color_fg.notZero() ?: column.getAcctColor()

        tv.setBackgroundColor(ac.color_bg) // may 0
        tv.setPaddingRelative(activity.acctPadLr, 0, activity.acctPadLr, 0)
    }

    private fun formatFeaturedTags() = column.whoFeaturedTags?.notEmpty()?.let { tagList ->
        SpannableStringBuilder().apply {
            append(activity.getString(R.string.featured_hashtags))
            append(":")
            tagList.forEach { tag ->
                append(" ")
                val tagWithSharp = "#" + tag.name
                val start = length
                append(tagWithSharp)
                val end = length
                tag.url?.notEmpty()?.let { url ->
                    val span = MyClickableSpan(
                        LinkInfo(url = url, tag = tag.name, caption = tagWithSharp)
                    )
                    setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
                }
            }
        }
    }

    private fun encodeAcctText(who: TootAccount, whoDetail: TootAccount?) = SpannableStringBuilder().apply {
        append("@")
        append(accessInfo.getFullAcct(who).pretty)
        if (whoDetail?.locked ?: who.locked) {
            append(" ")
            val emoji = EmojiMap.shortNameMap["lock"]
            if (emoji != null) {
                appendSpan("locked", emoji.createSpan(activity))
            } else {
                append("locked")
            }
        }

        if (who.bot) {
            append(" ")
            val emoji = EmojiMap.shortNameMap["robot_face"]
            if (emoji != null) {
                appendSpan("bot", emoji.createSpan(activity))
            } else {
                append("bot")
            }
        }

        if (who.suspended) {
            append(" ")
            val emoji = EmojiMap.shortNameMap["cross_mark"]
            if (emoji != null) {
                appendSpan("suspended", emoji.createSpan(activity))
            } else {
                append("suspended")
            }
        }
    }

    private fun encodeMisskeyExtra(whoDetail: TootAccount?) = SpannableStringBuilder().apply {
        var s = whoDetail?.location
        if (s?.isNotEmpty() == true) {
            if (isNotEmpty()) append('\n')
            appendSpan(
                activity.getString(R.string.location),
                EmojiImageSpan(
                    activity,
                    R.drawable.ic_location,
                    useColorShader = true
                )
            )
            append(' ')
            append(s)
        }
        s = whoDetail?.birthday
        if (s?.isNotEmpty() == true) {
            if (isNotEmpty()) append('\n')
            appendSpan(
                activity.getString(R.string.birthday),
                EmojiImageSpan(
                    activity,
                    R.drawable.ic_cake,
                    useColorShader = true
                )
            )
            append(' ')
            append(s)
        }
    }

    private fun showFields(who: TootAccount, fields: List<TootAccount.Field>) {
        llFields.visibility = View.VISIBLE

        // fieldsのnameにはカスタム絵文字が適用されるようになった
        // https://github.com/tootsuite/mastodon/pull/11350
        // fieldsのvalueはMisskeyならMFM、MastodonならHTML
        val fieldDecodeOptions = DecodeOptions(
            context = activity,
            decodeEmoji = true,
            linkHelper = accessInfo,
            short = true,
            emojiMapCustom = who.custom_emojis,
            emojiMapProfile = who.profile_emojis,
            mentionDefaultHostDomain = who
        )

        val nameTypeface = ActMain.timeline_font_bold
        val valueTypeface = ActMain.timelineFont

        for (item in fields) {

            //
            val nameView = MyTextView(activity)
            val nameLp = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            )
            val nameText = fieldDecodeOptions.decodeEmoji(item.name)
            val nameInvalidator = NetworkEmojiInvalidator(activity.handler, nameView)
            nameInvalidator.register(nameText)

            nameLp.topMargin = (density * 6f).toInt()
            nameView.layoutParams = nameLp
            nameView.text = nameText
            nameView.setTextColor(contentColor)
            nameView.typeface = nameTypeface
            nameView.movementMethod = MyLinkMovementMethod
            llFields.addView(nameView)

            // 値の方はHTMLエンコードされている
            val valueView = MyTextView(activity)
            val valueLp = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            )

            val valueText = fieldDecodeOptions.decodeHTML(item.value)
            if (item.verified_at > 0L) {
                valueText.append('\n')

                val start = valueText.length
                valueText.append(activity.getString(R.string.verified_at))
                valueText.append(": ")
                valueText.append(TootStatus.formatTime(activity, item.verified_at, false))
                val end = valueText.length

                val linkFgColor = PrefI.ipVerifiedLinkFgColor(activity.pref).notZero()
                    ?: (Color.BLACK or 0x7fbc99)

                valueText.setSpan(
                    ForegroundColorSpan(linkFgColor),
                    start,
                    end,
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                )
            }

            val valueInvalidator = NetworkEmojiInvalidator(activity.handler, valueView)
            valueInvalidator.register(valueText)

            valueLp.startMargin = (density * 32f).toInt()
            valueView.layoutParams = valueLp
            valueView.text = valueText
            valueView.setTextColor(contentColor)
            valueView.typeface = valueTypeface
            valueView.movementMethod = MyLinkMovementMethod

            if (item.verified_at > 0L) {
                val linkBgColor = PrefI.ipVerifiedLinkBgColor(activity.pref).notZero()
                    ?: (0x337fbc99)

                valueView.setBackgroundColor(linkBgColor)
            }

            llFields.addView(valueView)
        }
    }
}
