# wctxcleaner.py - check and clean dirty working directory
#
# Copyright 2011 Steve Borho <steve@borho.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

from __future__ import annotations

from .qtcore import (
    QObject,
    QThread,
    pyqtSignal,
    pyqtSlot,
)
from .qtgui import (
    QMessageBox,
    QWidget,
)

from mercurial import (
    cmdutil,
    error,
    hg,
)

from ..util.i18n import _
from . import (
    cmdcore,
    cmdui,
    qtlib,
    thgrepo,
)

def _checkchanged(repo):
    try:
        cmdutil.bailifchanged(repo)
        return False
    except error.Abort:
        return True


class CheckThread(QThread):
    def __init__(self, repo, parent):
        QThread.__init__(self, parent)
        self.repo = hg.repository(repo.ui, repo.root)
        self.results = (False, 1)
        self.canceled = False

    def run(self):
        self.repo.invalidate()
        self.repo.invalidatedirstate()
        unresolved = False
        for root, path, status in thgrepo.recursiveMergeStatus(self.repo):
            if self.canceled:
                return
            if status == b'u':
                unresolved = True
                break
        wctx = self.repo[None]
        try:
            dirty = _checkchanged(self.repo) or unresolved
            self.results = (dirty, len(wctx.parents()))
        except OSError:
            self.results = (True, len(wctx.parents()))

    def cancel(self):
        self.canceled = True


class WctxCleaner(QObject):

    checkStarted = pyqtSignal()
    checkFinished = pyqtSignal(bool, int)  # clean, parents

    def __init__(self, repoagent, parent=None):
        super().__init__(parent)
        assert parent is None or isinstance(parent, QWidget), parent
        self._repoagent = repoagent
        self._cmdsession = cmdcore.nullCmdSession()
        self._checkth = CheckThread(repoagent.rawRepo(), self)
        self._checkth.started.connect(self.checkStarted)
        self._checkth.finished.connect(self._onCheckFinished)
        self._clean = False

    @pyqtSlot()
    def check(self):
        """Check states of working directory asynchronously"""
        if self._checkth.isRunning():
            return
        self._checkth.start()

    def cancelCheck(self):
        self._checkth.cancel()
        self._checkth.wait()

    def isChecking(self):
        return self._checkth.isRunning()

    def isCheckCanceled(self):
        return self._checkth.canceled

    def isClean(self):
        return self._clean

    @pyqtSlot()
    def _onCheckFinished(self):
        dirty, parents = self._checkth.results
        self._clean = not dirty
        self.checkFinished.emit(not dirty, parents)

    @pyqtSlot(str)
    def runCleaner(self, cmd: str) -> None:
        """Clean working directory by the specified action"""
        if cmd == 'commit':
            self.launchCommitDialog()
        elif cmd == 'shelve':
            self.launchShelveDialog()
        elif cmd.startswith('discard'):
            confirm = cmd != 'discard:noconfirm'
            self.discardChanges(confirm)
        else:
            raise ValueError('unknown command: %s' % cmd)

    def launchCommitDialog(self):
        from tortoisehg.hgqt import commit
        dlg = commit.CommitDialog(self._repoagent, [], {}, self.parent())
        dlg.finished.connect(dlg.deleteLater)
        dlg.exec()
        self.check()

    def launchShelveDialog(self):
        from tortoisehg.hgqt import shelve
        dlg = shelve.ShelveDialog(self._repoagent, self.parent())
        dlg.finished.connect(dlg.deleteLater)
        dlg.exec()
        self.check()

    def discardChanges(self, confirm=True):
        if confirm:
            labels = [(QMessageBox.StandardButton.Yes, _('&Discard')),
                      (QMessageBox.StandardButton.No, _('Cancel'))]
            if not qtlib.QuestionMsgBox(_('Confirm Discard'),
                     _('Discard outstanding changes to working directory?'),
                     labels=labels, parent=self.parent()):
                return

        cmdline = ['update', '--clean', '--rev', '.']
        self._cmdsession = sess = self._repoagent.runCommand(cmdline, self)
        sess.commandFinished.connect(self._onCommandFinished)

    @pyqtSlot(int)
    def _onCommandFinished(self, ret):
        if ret == 0:
            self.check()
        else:
            cmdui.errorMessageBox(self._cmdsession, self.parent())
