package jp.juggler.subwaytooter.api.entity

import android.content.Context
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.api.entity.TootAccountRef.Companion.tootAccountRefOrNull
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.util.data.JsonObject
import jp.juggler.util.data.notEmpty
import jp.juggler.util.log.LogCategory

class TootNotification(
    val json: JsonObject,
    val id: EntityId,
    //	One of: "mention", "reblog", "favourite", "follow"
    val type: String,
    //	The Account sending the notification to the user
    val accountRef: TootAccountRef?,

    // The Status associated with the notification, if applicable
    // 投稿の更新により変更可能になる
    var status: TootStatus?,

    var reaction: TootReaction? = null,

    val reblog_visibility: TootVisibility,

    //	The time the notification was created
    private val created_at: String?,
    val time_created_at: Long,
) : TimelineItem() {

    val account: TootAccount?
        get() = accountRef?.get()

    companion object {
        @Suppress("unused")
        private val log = LogCategory("TootNotification")

        // 言及と返信
        const val TYPE_MENTION = "mention" // Mastodon,Misskey
        const val TYPE_REPLY = "reply" // Misskey (メンションとReplyは別の物らしい

        // ブーストとリノート
        const val TYPE_REBLOG = "reblog" // Mastodon
        const val TYPE_RENOTE = "renote" // Misskey
        const val TYPE_QUOTE = "quote" // Misskeyの引用Renote, fedibirdのquote

        // フォロー
        const val TYPE_FOLLOW = "follow" // Mastodon,Misskey
        const val TYPE_UNFOLLOW = "unfollow" // Mastodon,Misskey

        const val TYPE_FAVOURITE = "favourite"
        const val TYPE_REACTION = "reaction" // misskey
        const val TYPE_EMOJI_REACTION_PLEROMA = "pleroma:emoji_reaction" // pleroma

        const val TYPE_FOLLOW_REQUEST = "follow_request"
        const val TYPE_FOLLOW_REQUEST_MISSKEY = "receiveFollowRequest"

        const val TYPE_FOLLOW_REQUEST_ACCEPTED_MISSKEY = "followRequestAccepted"
        const val TYPE_POLL_VOTE_MISSKEY = "pollVote"

        // 投票
        const val TYPE_VOTE = "poll_vote"

        // (Mastodon 2.8)投票完了
        const val TYPE_POLL = "poll"

        const val TYPE_STATUS = "status"

        // (Mastodon 3.5.0rc1)
        const val TYPE_UPDATE = "update"
        const val TYPE_ADMIN_SIGNUP = "admin.sign_up"

        // (Mastodon 4.0)
        const val TYPE_ADMIN_REPORT = "admin.report"

        // (Fedibird)
        // https://github.com/fedibird/mastodon/blob/fedibird/app/controllers/api/v1/push/subscriptions_controller.rb#L55
        // https://github.com/fedibird/mastodon/blob/fedibird/app/models/notification.rb
        const val TYPE_EMOJI_REACTION = "emoji_reaction"
        const val TYPE_STATUS_REFERENCE = "status_reference"
        const val TYPE_SCHEDULED_STATUS = "scheduled_status"

        private fun tootNotificationMisskey(parser: TootParser, src: JsonObject): TootNotification {
            // Misskeyの通知APIはページネーションをIDでしか行えない
            // これは改善される予定 https://github.com/syuilo/misskey/issues/2275

            val created_at: String? = src.string("createdAt")

            val accountRef = tootAccountRefOrNull(
                parser,
                parser.account(src.jsonObject("user"))
            )

            val reaction: TootReaction? = src.string("reaction")
                ?.notEmpty()
                ?.let { TootReaction.parseMisskey(it) }

            return TootNotification(
                json = src,
                id = EntityId.mayDefault(src.string("id")),
                type = src.stringOrThrow("type"),
                accountRef = accountRef,
                status = parser.status(src.jsonObject("note")),
                reaction = reaction,
                reblog_visibility = TootVisibility.Unknown,
                created_at = created_at,
                time_created_at = TootStatus.parseTime(created_at),
            )
        }

        private fun tootNotificationMastodon(
            parser: TootParser,
            src: JsonObject,
        ): TootNotification {

            val created_at: String? = src.string("created_at")

            val accountRef: TootAccountRef? =
                tootAccountRefOrNull(parser, parser.account(src.jsonObject("account")))

            val status: TootStatus? = parser.status(src.jsonObject("status"))

            val reaction: TootReaction? = src.jsonObject("emoji_reaction")
                ?.notEmpty()
                ?.let { TootReaction.parseFedibird(it) }
                ?: src.string("emoji")?.let { TootReaction(name = it) } // pleroma unicode emoji

            // fedibird
            // https://github.com/fedibird/mastodon/blob/7974fd3c7ec11ea9f7bef4ad7f4009fff53f62af/app/serializers/rest/notification_serializer.rb#L9
            val visibilityString = when {
                src.boolean("limited") == true -> "limited"
                else -> src.string("reblog_visibility")
            }

            val reblog_visibility = TootVisibility.parseMastodon(visibilityString)
                ?: TootVisibility.Unknown

            return TootNotification(
                json = src,
                id = EntityId.mayDefault(src.string("id")),
                type = src.stringOrThrow("type"),
                accountRef = accountRef,
                status = status,
                reaction = reaction,
                reblog_visibility = reblog_visibility,
                created_at = created_at,
                time_created_at = TootStatus.parseTime(created_at),
            )
        }

        fun tootNotification(parser: TootParser, src: JsonObject): TootNotification =
            when (parser.serviceType) {
                ServiceType.MISSKEY -> tootNotificationMisskey(parser, src)
                else -> tootNotificationMastodon(parser, src)
            }
    }

    override fun getOrderId() = id

    fun getNotificationLine(context: Context): String {

        val name = when (PrefB.bpShowAcctInSystemNotification.value) {
            true -> accountRef?.get()?.acct?.pretty?.notEmpty()?.let { "@$it" }
            else -> accountRef?.decoded_display_name
        } ?: "?"

        return when (type) {
            TYPE_MENTION,
            TYPE_REPLY,
            -> context.getString(R.string.display_name_replied_by, name)

            TYPE_RENOTE,
            TYPE_REBLOG,
            -> context.getString(R.string.display_name_boosted_by, name)

            TYPE_QUOTE,
            -> context.getString(R.string.display_name_quoted_by, name)

            TYPE_STATUS,
            -> context.getString(R.string.display_name_posted_by, name)

            TYPE_UPDATE,
            -> context.getString(R.string.display_name_updates_post, name)

            TYPE_STATUS_REFERENCE,
            -> context.getString(R.string.display_name_references_post, name)

            TYPE_FOLLOW,
            -> context.getString(R.string.display_name_followed_by, name)

            TYPE_UNFOLLOW,
            -> context.getString(R.string.display_name_unfollowed_by, name)

            TYPE_ADMIN_SIGNUP,
            -> context.getString(R.string.display_name_signed_up, name)

            TYPE_ADMIN_REPORT,
            -> context.getString(R.string.display_name_report, name)

            TYPE_FAVOURITE,
            -> context.getString(R.string.display_name_favourited_by, name)

            TYPE_EMOJI_REACTION_PLEROMA,
            TYPE_EMOJI_REACTION,
            TYPE_REACTION,
            -> arrayOf(
                context.getString(R.string.display_name_reaction_by, name),
                reaction?.name
            ).mapNotNull { it.notEmpty() }.joinToString(" ")

            TYPE_VOTE,
            TYPE_POLL_VOTE_MISSKEY,
            -> context.getString(R.string.display_name_voted_by, name)

            TYPE_FOLLOW_REQUEST,
            TYPE_FOLLOW_REQUEST_MISSKEY,
            -> context.getString(R.string.display_name_follow_request_by, name)

            TYPE_FOLLOW_REQUEST_ACCEPTED_MISSKEY,
            -> context.getString(R.string.display_name_follow_request_accepted_by, name)

            TYPE_POLL,
            -> context.getString(R.string.end_of_polling_from, name)

            else -> context.getString(R.string.unknown_notification_from, name) + " :" + type
        }
    }
}
