package jp.juggler.subwaytooter.action

import android.content.Intent
import androidx.appcompat.app.AlertDialog
import jp.juggler.subwaytooter.*
import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.dialog.ActionsDialog
import jp.juggler.subwaytooter.dialog.DlgConfirm
import jp.juggler.subwaytooter.dialog.pickAccount
import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.emptyCallback
import jp.juggler.util.*
import okhttp3.Request
import java.util.*

fun ActMain.clickStatusDelete(
    accessInfo: SavedAccount,
    status: TootStatus?,
) {
    status ?: return
    AlertDialog.Builder(this)
        .setMessage(getString(R.string.confirm_delete_status))
        .setNegativeButton(R.string.cancel, null)
        .setPositiveButton(R.string.ok) { _, _ -> statusDelete(accessInfo, status.id) }
        .show()
}

fun ActMain.clickBookmark(accessInfo: SavedAccount, status: TootStatus, willToast: Boolean) {
    if (accessInfo.isPseudo) {
        bookmarkFromAnotherAccount(accessInfo, status)
    } else {
        // トグル動作
        val bSet = !status.bookmarked

        bookmark(
            accessInfo,
            status,
            CrossAccountMode.SameAccount,
            bSet = bSet,
            callback = when {
                !willToast -> emptyCallback
                // 簡略表示なら結果をトースト表示
                bSet -> bookmarkCompleteCallback
                else -> unbookmarkCompleteCallback
            },
        )
    }
}

fun ActMain.clickFavourite(accessInfo: SavedAccount, status: TootStatus, willToast: Boolean) {
    if (accessInfo.isPseudo) {
        favouriteFromAnotherAccount(accessInfo, status)
    } else {
        // トグル動作
        val bSet = !status.favourited

        favourite(
            accessInfo,
            status,
            CrossAccountMode.SameAccount,
            bSet = bSet,
            callback = when {
                !willToast -> emptyCallback
                // 簡略表示なら結果をトースト表示
                bSet -> favouriteCompleteCallback
                else -> unfavouriteCompleteCallback
            },
        )
    }
}

fun ActMain.clickScheduledToot(accessInfo: SavedAccount, item: TootScheduled, column: Column) {
    ActionsDialog()
        .addAction(getString(R.string.edit)) {
            scheduledPostEdit(accessInfo, item)
        }
        .addAction(getString(R.string.delete)) {
            scheduledPostDelete(accessInfo, item) {
                column.onScheduleDeleted(item)
                showToast(false, R.string.scheduled_post_deleted)
            }
        }
        .show(this)
}

fun ActMain.launchActText(intent: Intent) = arActText.launch(intent)

///////////////////////////////////////////////////////////////
// お気に入りの非同期処理
fun ActMain.favourite(
    accessInfo: SavedAccount,
    statusArg: TootStatus,
    crossAccountMode: CrossAccountMode,
    callback: () -> Unit,
    bSet: Boolean = true,
    bConfirmed: Boolean = false,
) {
    if (appState.isBusyFav(accessInfo, statusArg)) {
        showToast(false, R.string.wait_previous_operation)
        return
    }

    // 必要なら確認を出す
    if (!bConfirmed && accessInfo.isMastodon) {
        DlgConfirm.open(
            this,
            getString(
                when (bSet) {
                    true -> R.string.confirm_favourite_from
                    else -> R.string.confirm_unfavourite_from
                },
                AcctColor.getNickname(accessInfo)
            ),
            object : DlgConfirm.Callback {

                override fun onOK() {
                    favourite(
                        accessInfo,
                        statusArg,
                        crossAccountMode,
                        callback,
                        bSet = bSet,
                        bConfirmed = true
                    )
                }

                override var isConfirmEnabled: Boolean
                    get() = when (bSet) {
                        true -> accessInfo.confirm_favourite
                        else -> accessInfo.confirm_unfavourite
                    }
                    set(value) {
                        when (bSet) {
                            true -> accessInfo.confirm_favourite = value
                            else -> accessInfo.confirm_unfavourite = value
                        }
                        accessInfo.saveSetting()
                        reloadAccountSetting(accessInfo)
                    }
            })
        return
    }

    //
    appState.setBusyFav(accessInfo, statusArg)

    // ファボ表示を更新中にする
    showColumnMatchAccount(accessInfo)

    launchMain {
        var resultStatus: TootStatus? = null
        val result = runApiTask(
            accessInfo,
            progressStyle = ApiTask.PROGRESS_NONE
        ) { client ->
            val targetStatus = if (crossAccountMode.isRemote) {
                val (result, status) = client.syncStatus(accessInfo, statusArg)
                status ?: return@runApiTask result
                if (status.favourited) {
                    return@runApiTask TootApiResult(getString(R.string.already_favourited))
                }
                status
            } else {
                statusArg
            }

            if (accessInfo.isMisskey) {
                client.request(
                    if (bSet) {
                        "/api/notes/favorites/create"
                    } else {
                        "/api/notes/favorites/delete"
                    },
                    accessInfo.putMisskeyApiToken().apply {
                        put("noteId", targetStatus.id.toString())
                    }
                        .toPostRequestBuilder()
                )?.also { result ->
                    // 正常レスポンスは 204 no content
                    // 既にお気に入り済みならエラー文字列に "already favorited" が返る
                    if (result.response?.code == 204 ||
                        result.error?.contains("already favorited") == true ||
                        result.error?.contains("already not favorited") == true
                    ) {
                        // 成功した
                        resultStatus = targetStatus.apply { favourited = bSet }
                    }
                }
            } else {
                client.request(
                    "/api/v1/statuses/${targetStatus.id}/${if (bSet) "favourite" else "unfavourite"}",
                    "".toFormRequestBody().toPost()
                )?.also { result ->
                    resultStatus = TootParser(this, accessInfo).status(result.jsonObject)
                }
            }
        }

        appState.resetBusyFav(accessInfo, statusArg)

        if (result != null) {
            when (val newStatus = resultStatus) {
                null -> showToast(true, result.error)
                else -> {
                    val oldCount = statusArg.favourites_count
                    val newCount = newStatus.favourites_count
                    if (oldCount != null && newCount != null) {
                        if (accessInfo.isMisskey) {
                            newStatus.favourited = bSet
                        }
                        if (bSet && newStatus.favourited && newCount <= oldCount) {
                            // 星をつけたのにカウントが上がらないのは違和感あるので、表示をいじる
                            newStatus.favourites_count = oldCount + 1L
                        } else if (!bSet && !newStatus.favourited && newCount >= oldCount) {
                            // 星を外したのにカウントが下がらないのは違和感あるので、表示をいじる
                            // 0未満にはならない
                            newStatus.favourites_count =
                                if (oldCount < 1L) 0L else oldCount - 1L
                        }
                    }

                    for (column in appState.columnList) {
                        column.findStatus(
                            accessInfo.apDomain,
                            newStatus.id
                        ) { account, status ->

                            // 同タンス別アカウントでもカウントは変化する
                            status.favourites_count = newStatus.favourites_count

                            // 同アカウントならfav状態を変化させる
                            if (accessInfo == account) {
                                status.favourited = newStatus.favourited
                            }

                            true
                        }
                    }
                    callback()
                }
            }
        }

        // 結果に関わらず、更新中状態から復帰させる
        showColumnMatchAccount(accessInfo)
    }
}

// アカウントを選んでお気に入り
fun ActMain.favouriteFromAnotherAccount(
    timelineAccount: SavedAccount,
    status: TootStatus?,
) {
    status ?: return
    launchMain {
        pickAccount(
            bAllowPseudo = false,
            bAuto = false,
            message = getString(R.string.account_picker_favourite),
            accountListArg = accountListNonPseudo(timelineAccount.apDomain)
        )?.let { action_account ->
            favourite(
                action_account,
                status,
                calcCrossAccountMode(timelineAccount, action_account),
                callback = favouriteCompleteCallback
            )
        }
    }
}

////////////////////////////////////////////////////////////////

// お気に入りの非同期処理
fun ActMain.bookmark(
    accessInfo: SavedAccount,
    statusArg: TootStatus,
    crossAccountMode: CrossAccountMode,
    callback: () -> Unit,
    bSet: Boolean = true,
    bConfirmed: Boolean = false,
) {
    if (appState.isBusyFav(accessInfo, statusArg)) {
        showToast(false, R.string.wait_previous_operation)
        return
    }
    if (accessInfo.isMisskey) {
        showToast(false, R.string.misskey_account_not_supported)
        return
    }

    // 必要なら確認を出す
    // ブックマークは解除する時だけ確認する
    if (!bConfirmed && !bSet) {
        DlgConfirm.openSimple(
            this,
            getString(
                R.string.confirm_unbookmark_from,
                AcctColor.getNickname(accessInfo)
            )
        ) {
            bookmark(

                accessInfo,
                statusArg,
                crossAccountMode,
                callback,
                bSet = bSet,
                bConfirmed = true
            )
        }
        return
    }

    //
    appState.setBusyBookmark(accessInfo, statusArg)

    // ファボ表示を更新中にする
    showColumnMatchAccount(accessInfo)

    //
    launchMain {
        var resultStatus: TootStatus? = null
        val result = runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
            val targetStatus = if (crossAccountMode.isRemote) {
                val (result, status) = client.syncStatus(accessInfo, statusArg)
                status ?: return@runApiTask result
                if (status.bookmarked) {
                    return@runApiTask TootApiResult(getString(R.string.already_bookmarked))
                }
                status
            } else {
                statusArg
            }

            client.request(
                "/api/v1/statuses/${targetStatus.id}/${if (bSet) "bookmark" else "unbookmark"}",
                "".toFormRequestBody().toPost()
            )?.also { result ->
                resultStatus = TootParser(this, accessInfo).status(result.jsonObject)
            }
        }

        appState.resetBusyBookmark(accessInfo, statusArg)

        if (result != null) {
            when (val newStatus = resultStatus) {
                null -> showToast(true, result.error)
                else -> {
                    for (column in appState.columnList) {
                        column.findStatus(
                            accessInfo.apDomain,
                            newStatus.id
                        ) { account, status ->

                            // 同アカウントならブックマーク状態を伝播する
                            if (accessInfo == account) {
                                status.bookmarked = newStatus.bookmarked
                            }

                            true
                        }
                    }
                    callback()
                }
            }
        }
        // 結果に関わらず、更新中状態から復帰させる
        showColumnMatchAccount(accessInfo)
    }
}

// アカウントを選んでブックマーク
fun ActMain.bookmarkFromAnotherAccount(
    timelineAccount: SavedAccount,
    status: TootStatus?,
) {
    status ?: return

    launchMain {
        pickAccount(
            bAllowPseudo = false,
            bAuto = false,
            message = getString(R.string.account_picker_bookmark),
            accountListArg = accountListNonPseudo(timelineAccount.apDomain)
        )?.let { action_account ->
            bookmark(
                action_account,
                status,
                calcCrossAccountMode(timelineAccount, action_account),
                callback = bookmarkCompleteCallback
            )
        }
    }
}

///////////////////////////////////////////////////////////////

fun ActMain.statusDelete(
    accessInfo: SavedAccount,
    statusId: EntityId,
) {
    launchMain {
        runApiTask(accessInfo) { client ->
            if (accessInfo.isMisskey) {
                client.request(
                    "/api/notes/delete",
                    accessInfo.putMisskeyApiToken().apply {
                        put("noteId", statusId)
                    }
                        .toPostRequestBuilder()
                )
                // 204 no content
            } else {
                client.request(
                    "/api/v1/statuses/$statusId",
                    Request.Builder().delete()
                )
            }
        }?.let { result ->

            if (result.jsonObject != null) {
                showToast(false, R.string.delete_succeeded)
                for (column in appState.columnList) {
                    column.onStatusRemoved(accessInfo.apDomain, statusId)
                }
            } else {
                showToast(false, result.error)
            }
        }
    }
}

////////////////////////////////////////
// profile pin

fun ActMain.statusPin(
    accessInfo: SavedAccount,
    status: TootStatus?,
    bSet: Boolean,
) {
    status ?: return

    launchMain {
        var resultStatus: TootStatus? = null
        runApiTask(
            accessInfo,
            progressPrefix = getString(R.string.profile_pin_progress)
        ) { client ->
            client.request(
                "/api/v1/statuses/${status.id}/${if (bSet) "pin" else "unpin"}",
                "".toFormRequestBody().toPost()
            )?.also { result ->
                resultStatus = TootParser(this, accessInfo).status(result.jsonObject)
            }
        }?.let { result ->
            when (val newStatus = resultStatus) {
                null -> showToast(true, result.error)
                else -> {
                    for (column in appState.columnList) {
                        if (accessInfo == column.accessInfo) {
                            column.findStatus(
                                accessInfo.apDomain,
                                newStatus.id
                            ) { _, status ->
                                status.pinned = bSet
                                true
                            }
                        }
                    }
                    showColumnMatchAccount(accessInfo)
                }
            }
        }
    }
}

// 投稿画面を開く。初期テキストを指定する
fun ActMain.statusRedraft(
    accessInfo: SavedAccount,
    status: TootStatus?,
) {
    status ?: return

    completionHelper.closeAcctPopup()

    when {
        accessInfo.isMisskey ->
            openActPostImpl(
                accessInfo.db_id,
                redraftStatus = status,
                replyStatus = status.reply
            )

        status.in_reply_to_id == null ->
            openActPostImpl(
                accessInfo.db_id,
                redraftStatus = status
            )

        else -> launchMain {
            var resultStatus: TootStatus? = null
            runApiTask(accessInfo) { client ->
                client.request("/api/v1/statuses/${status.in_reply_to_id}")
                    ?.also { resultStatus = TootParser(this, accessInfo).status(it.jsonObject) }
            }?.let { result ->
                when (val replyStatus = resultStatus) {
                    null -> showToast(
                        true,
                        "${getString(R.string.cant_sync_toot)} : ${result.error ?: "(no information)"}"
                    )
                    else -> openActPostImpl(
                        accessInfo.db_id,
                        redraftStatus = status,
                        replyStatus = replyStatus
                    )
                }
            }
        }
    }
}

fun ActMain.scheduledPostDelete(
    accessInfo: SavedAccount,
    item: TootScheduled,
    bConfirmed: Boolean = false,
    callback: () -> Unit,
) {
    val act = this@scheduledPostDelete
    if (!bConfirmed) {
        DlgConfirm.openSimple(
            act,
            getString(R.string.scheduled_status_delete_confirm)
        ) {
            scheduledPostDelete(
                accessInfo,
                item,
                bConfirmed = true,
                callback = callback
            )
        }
        return
    }
    launchMain {
        runApiTask(accessInfo) { client ->
            client.request(
                "/api/v1/scheduled_statuses/${item.id}",
                Request.Builder().delete()
            )
        }?.let { result ->
            when (val error = result.error) {
                null -> callback()
                else -> showToast(true, error)
            }
        }
    }
}

fun ActMain.scheduledPostEdit(
    accessInfo: SavedAccount,
    item: TootScheduled,
) {
    launchMain {
        var resultStatus: TootStatus? = null
        runApiTask(accessInfo) { client ->
            val replyStatusId = item.inReplyToId ?: return@runApiTask TootApiResult()
            client.request("/api/v1/statuses/$replyStatusId")?.also { result ->
                resultStatus = TootParser(this, accessInfo).status(result.jsonObject)
            }
        }?.let { result ->
            when (val error = result.error) {
                null -> openActPostImpl(
                    accessInfo.db_id,
                    scheduledStatus = item,
                    replyStatus = resultStatus
                )

                else -> showToast(true, error)
            }
        }
    }
}
