package com.huangzj.protoplugin.vcs

import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.vcs.checkin.CheckinHandler
import com.intellij.openapi.vcs.checkin.CommitCheck
import com.intellij.openapi.vcs.checkin.CommitInfo
import com.intellij.openapi.vcs.checkin.CommitProblem
import com.intellij.openapi.vcs.checkin.TextCommitProblem
import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.abs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

/**
 * 直接实现 [CommitCheck] 且顺序为 [CommitCheck.ExecutionOrder.EARLY]，避免落在默认 [CheckinHandler] 的 LATE 阶段。
 *
 * 非模态提交里 LATE 失败会走 ABORTED → Cancelled，可能伴随文档/部分提交状态的恢复，表现为「文件被回退」；
 * EARLY 失败为 EARLY_FAILED → CommitChecksResult.Failed()，在跑完 MODIFICATION/saveAllDocuments 之前即中止，一般不触达该恢复路径。
 */
class TimeSkewCommitCheckHandler(private val project: Project?) : CheckinHandler(), CommitCheck, DumbAware {

    companion object {
        const val MAX_SKEW_MS: Long = 60_000L
    }

    override fun getExecutionOrder(): CommitCheck.ExecutionOrder = CommitCheck.ExecutionOrder.EARLY

    override fun isEnabled(): Boolean = true

    override suspend fun runCheck(commitInfo: CommitInfo): CommitProblem? {
        val reference =
            withContext(Dispatchers.IO) {
                try {
                    TimeSkewChecker.fetchReferenceEpochMillis()
                } catch (_: Exception) {
                    null
                }
            }
        if (reference == null) {
            return null
        }
        val local = System.currentTimeMillis()
        if (abs(local - reference) <= MAX_SKEW_MS) {
            return null
        }

        val app = ApplicationManager.getApplication()
        val picked =
            if (app.isDispatchThread) {
                showSkewWarning(project)
            } else {
                val choice = AtomicInteger(0)
                app.invokeAndWait(
                    { choice.set(showSkewWarning(project)) },
                    ModalityState.defaultModalityState(),
                )
                choice.get()
            }

        return if (picked == 0) {
            null
        } else {
            TextCommitProblem("已取消提交：系统时间与网络时间相差超过 1 分钟。")
        }
    }

    private fun showSkewWarning(project: Project?): Int {
        return Messages.showDialog(
            project,
            """
            本机时间与网络标准时间相差超过 1 分钟，可能导致提交记录时间异常。

            建议校准：Windows「设置 → 时间和语言 → 日期和时间」中开启自动设置时间。

            点击「确定」继续提交；点击「取消」中止提交（不会回退本地文件）。
            """.trimIndent(),
            "系统时间可能不准确",
            arrayOf("确定", "取消"),
            0,
            Messages.getWarningIcon(),
            null,
        )
    }
}
