# -----------------------------------------------------------------------------
# Copyright (C) 2026 Iliyas Jorio.
# This file is part of GitFourchette, distributed under the GNU GPL v3.
# For full terms, see the included LICENSE file.
# -----------------------------------------------------------------------------

from gitfourchette.application import GFApplication
from gitfourchette.blameview.blamemodel import BlameModel, Revision
from gitfourchette.blameview.blamescrubber import BlameScrubber
from gitfourchette.blameview.blametextedit import BlameTextEdit
from gitfourchette.blameview.blamebusyspinner import BlameBusySpinner
from gitfourchette.graphview.commitlogmodel import CommitLogModel
from gitfourchette.localization import *
from gitfourchette.nav import NavLocator, NavHistory, NavFlags
from gitfourchette.qt import *
from gitfourchette.tasks import RepoTaskRunner
from gitfourchette.toolbox import *


class BlameWindow(QWidget):
    exploreCommit = Signal(NavLocator)

    _currentBlameWindows = []
    "Currently open BlameWindows"

    def __init__(self, blameModel: BlameModel):
        # DON'T parent the window so it doesn't sit on top of MainWindow on Wayland
        super().__init__(None)

        # Keep reference around to avoid being GC'd instantly
        BlameWindow._currentBlameWindows.append(self)

        self.setObjectName("BlameWindow")

        self.model = blameModel
        self.taskRunner = RepoTaskRunner(self)
        self.navHistory = NavHistory()

        self.textEdit = BlameTextEdit(blameModel, self)
        self.textEdit.setUpAsDetachedWindow()

        self.scrubber = BlameScrubber(blameModel, self)
        self.scrubber.activated.connect(self.onScrubberActivated)

        self.jumpButton = QToolButton()
        self.jumpButton.setText(_("Jump"))
        self.jumpButton.setToolTip(_("View this commit in the repo"))
        self.jumpButton.setIcon(stockIcon("go-window@20px"))
        self.jumpButton.clicked.connect(lambda: self.jumpToCommit())

        oldNewTipTemplate = (f"<p style='white-space: pre'>{{0}}<br>"
                             f"<span style='color: {mutedToolTipColorHex()}'>({_('Shift+Click:')} {{1}})</span>")

        self.olderButton = QToolButton()
        self.olderButton.setText(_("Older"))
        self.olderButton.clicked.connect(self.goOlder)
        self.olderButton.setToolTip(oldNewTipTemplate.format(_("Go to next older revision"), _("Jump to bottom")))
        self.olderButton.setIcon(stockIcon("go-older"))

        self.newerButton = QToolButton()
        self.newerButton.setText(_("Newer"))
        self.newerButton.clicked.connect(self.goNewer)
        self.newerButton.setToolTip(oldNewTipTemplate.format(_("Go to next newer revision"), _("Jump to top")))
        self.newerButton.setIcon(stockIcon("go-newer"))

        self.backButton = QToolButton()
        self.backButton.setText(_("Back"))
        self.backButton.clicked.connect(self.goBack)
        self.backButton.setToolTip(_("Navigate back"))
        self.backButton.setIcon(stockIcon("back"))

        self.forwardButton = QToolButton()
        self.forwardButton.setText(_("Forward"))
        self.forwardButton.clicked.connect(self.goForward)
        self.forwardButton.setToolTip(_("Navigate forward"))
        self.forwardButton.setIcon(stockIcon("forward"))

        topBar = QToolBar()
        topBar.setIconSize(QSize(20, 20))
        topBar.addWidget(self.backButton)
        topBar.addWidget(self.forwardButton)
        topBar.addSeparator()
        topBar.addWidget(self.newerButton)
        topBar.addWidget(self.olderButton)
        topBar.addWidget(self.scrubber)
        topBar.addWidget(self.jumpButton)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(QMargins())
        layout.addWidget(topBar)
        layout.addWidget(self.textEdit.searchBar)
        layout.addWidget(self.textEdit)
        layout.setSpacing(0)

        self.textEdit.showRevision.connect(self.showRevision)
        self.textEdit.jumpToCommit.connect(self.jumpToCommit)

        self.setWindowModality(Qt.WindowModality.NonModal)
        self.setWindowFlags(Qt.WindowType.Window)
        self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)

        self.busySpinner = BlameBusySpinner(self.textEdit)
        self.textEdit.sizeChanged.connect(self.busySpinner.reposition)

        app = GFApplication.instance()
        app.mouseSideButtonPressed.connect(self.onMouseSideButtonPressed)

    # -------------------------------------------------------------------------
    # Event filters & handlers

    def onMouseSideButtonPressed(self, forward: bool):
        if not self.isActiveWindow():
            return
        delta = [-1, 1][forward]
        self.goBackOrForwardDelta(delta)

    def closeEvent(self, event: QCloseEvent):
        assert self in self._currentBlameWindows
        self._currentBlameWindows.remove(self)

        self.scrubber.scrubberDelegate.prepareForDeletion()
        self.scrubber.deleteLater()

        self.taskRunner.prepareForDeletion()
        self.model.prepareForDeletion()

        super().closeEvent(event)

    # -------------------------------------------------------------------------

    def saveFilePosition(self) -> NavLocator:
        currentLocator = self.textEdit.preciseLocator()
        self.navHistory.push(currentLocator)
        return currentLocator

    def showRevision(self, revision: Revision, saveAndTransposePosition=True):
        from gitfourchette.tasks.blametasks import BlameRevision
        BlameRevision.invoke(self, revision, saveAndTransposePosition)

    def syncNavButtons(self):
        index = self.scrubber.currentIndex()
        count = self.scrubber.count()

        self.newerButton.setEnabled(index != 0 and count >= 2)
        self.olderButton.setEnabled(index != count - 1)

        self.backButton.setEnabled(self.navHistory.canGoBack())
        self.forwardButton.setEnabled(self.navHistory.canGoForward())

    def goNewer(self):
        if QGuiApplication.keyboardModifiers() == Qt.KeyboardModifier.ShiftModifier:
            topRevision = self.model.revList.sequence[0]
            self.showRevision(topRevision)
        else:
            self.goNewerOrOlder(-1)

    def goOlder(self):
        if QGuiApplication.keyboardModifiers() == Qt.KeyboardModifier.ShiftModifier:
            bottomRevision = self.model.revList.sequence[-1]
            self.showRevision(bottomRevision)
        else:
            self.goNewerOrOlder(1)

    def goBack(self):
        self.goBackOrForwardDelta(-1)

    def goForward(self):
        self.goBackOrForwardDelta(1)

    def goNewerOrOlder(self, delta: int):
        index = self.scrubber.currentIndex()
        index += delta
        assert 0 <= index < self.scrubber.count()
        revision = self.getRevisionFromScrubberRow(index)
        self.showRevision(revision)

    def goBackOrForwardDelta(self, delta: int):
        self.saveFilePosition()
        locator = self.navHistory.navigateDelta(delta)
        if not locator:
            return
        revision = self.model.revList.revisionForCommit(locator.commit)
        self.showRevision(revision, saveAndTransposePosition=False)

    def onScrubberActivated(self, index: int):
        revision = self.getRevisionFromScrubberRow(index)
        self.showRevision(revision)

    def getRevisionFromScrubberRow(self, index: int) -> Revision:
        return self.scrubber.itemData(index, CommitLogModel.Role.BlameRevision)

    def jumpToCommit(self, locator: NavLocator = NavLocator.Empty):
        if locator == NavLocator.Empty:
            locator = self.model.currentRevision.toLocator()
        locator = locator.withExtraFlags(NavFlags.ActivateWindow)
        self.exploreCommit.emit(locator)
