"""
novelWriter – Project Settings Dialog Class Tester
==================================================

This file is a part of novelWriter
Copyright 2018–2023, Veronica Berglyd Olsen

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""

import pytest

from tools import C, getGuiItem, buildTestProject

from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog, QAction, QColorDialog

from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwItemType
from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.projsettings import GuiProjectSettings

KEY_DELAY = 1


@pytest.mark.gui
def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
    """Test the main dialog class. Saving settings is not tested in this
    test, but are instead tested in the individual tab tests.
    """
    # Block the GUI blocking thread
    monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None)
    monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted)

    # Check that we cannot open when there is no project
    nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
    assert getGuiItem("GuiProjectSettings") is None

    # Pretend we have a project
    SHARED.project._valid = True
    SHARED.project.data.setSpellLang("en")

    # Get the dialog object
    nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
    qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)

    projEdit = getGuiItem("GuiProjectSettings")
    assert isinstance(projEdit, GuiProjectSettings)
    projEdit.show()
    qtbot.addWidget(projEdit)

    # Switch Tabs
    projEdit._focusTab(GuiProjectSettings.TAB_REPLACE)
    assert projEdit._tabBox.currentWidget() == projEdit.tabReplace

    projEdit._focusTab(GuiProjectSettings.TAB_IMPORT)
    assert projEdit._tabBox.currentWidget() == projEdit.tabImport

    projEdit._focusTab(GuiProjectSettings.TAB_STATUS)
    assert projEdit._tabBox.currentWidget() == projEdit.tabStatus

    projEdit._focusTab(GuiProjectSettings.TAB_MAIN)
    assert projEdit._tabBox.currentWidget() == projEdit.tabMain

    # Clean Up
    projEdit._doClose()
    # qtbot.stop()

# END Test testDlgProjSettings_Dialog


@pytest.mark.gui
def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
    """Test the main tab of the project settings dialog."""
    monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])

    # Create new project
    buildTestProject(nwGUI, projPath)
    mockRnd.reset()
    CONFIG.setBackupPath(fncPath)

    # Set some values
    theProject = SHARED.project
    theProject.data.setSpellLang("en")
    theProject.data.setAuthor("Jane Smith")
    theProject.data.setAutoReplace({"A": "B", "C": "D"})

    # Create Dialog
    projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN)
    projSettings.show()
    qtbot.addWidget(projSettings)

    # Settings Tab
    # ============

    tabMain = projSettings.tabMain

    assert tabMain.editName.text() == "New Project"
    assert tabMain.editTitle.text() == "New Novel"
    assert tabMain.editAuthor.text() == "Jane Smith"
    assert tabMain.spellLang.currentData() == "en"
    assert tabMain.doBackup.isChecked() is False

    tabMain.editName.setText("")
    for c in "Project Name":
        qtbot.keyClick(tabMain.editName, c, delay=KEY_DELAY)
    tabMain.editTitle.setText("")
    for c in "Project Title":
        qtbot.keyClick(tabMain.editTitle, c, delay=KEY_DELAY)

    tabMain.editAuthor.clear()
    for c in "Jane Doe":
        qtbot.keyClick(tabMain.editAuthor, c, delay=KEY_DELAY)

    assert tabMain.editName.text() == "Project Name"
    assert tabMain.editTitle.text() == "Project Title"
    assert tabMain.editAuthor.text() == "Jane Doe"

    projSettings._doSave()
    assert theProject.data.name == "Project Name"
    assert theProject.data.title == "Project Title"
    assert theProject.data.author == "Jane Doe"

    # Clean up
    projSettings._doClose()
    # qtbot.stop()

# END Test testDlgProjSettings_Main


@pytest.mark.gui
def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
    """Test the status and importance tabs of the project settings
    dialog.
    """
    monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
    monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])

    # Create new project
    mockRnd.reset()
    buildTestProject(nwGUI, projPath)
    CONFIG.setBackupPath(fncPath)

    # Set some values
    theProject = SHARED.project
    theProject.tree[C.hTitlePage].setStatus(C.sFinished)
    theProject.tree[C.hChapterDoc].setStatus(C.sDraft)
    theProject.tree[C.hSceneDoc].setStatus(C.sDraft)

    nwGUI.projView.projTree.setSelectedHandle(C.hPlotRoot)
    nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
    nwGUI.projView.projTree.setSelectedHandle(C.hCharRoot)
    nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
    nwGUI.projView.projTree.setSelectedHandle(C.hWorldRoot)
    nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)

    hPlotNote = "0000000000010"
    hCharNote = "0000000000011"
    hWorldNote = "0000000000012"

    theProject.tree[hPlotNote].setImport(C.iMajor)
    theProject.tree[hCharNote].setImport(C.iMajor)
    theProject.tree[hWorldNote].setImport(C.iMain)

    # Create Dialog
    projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_STATUS)
    projSettings.show()
    qtbot.addWidget(projSettings)

    # Status Tab
    # ==========

    tabStatus = projSettings.tabStatus

    assert tabStatus.colChanged is False
    assert tabStatus.getNewList() == ([], [])
    assert tabStatus.listBox.topLevelItemCount() == 4

    # Can't delete the first item (it's in use)
    tabStatus.listBox.clearSelection()
    tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
    qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
    assert tabStatus.listBox.topLevelItemCount() == 4

    # Can delete the second item
    tabStatus.listBox.clearSelection()
    tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(1))
    qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
    assert tabStatus.listBox.topLevelItemCount() == 3

    # Add a new item
    with monkeypatch.context() as mp:
        mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
        qtbot.mouseClick(tabStatus.addButton, Qt.LeftButton)
        tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
        for _ in range(8):
            qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=KEY_DELAY)
        for c in "Final":
            qtbot.keyClick(tabStatus.editName, c, delay=KEY_DELAY)
        qtbot.mouseClick(tabStatus.colButton, Qt.LeftButton)
        qtbot.mouseClick(tabStatus.saveButton, Qt.LeftButton)
        assert tabStatus.listBox.topLevelItemCount() == 4

    assert tabStatus.colChanged is True
    assert tabStatus.getNewList() == (
        [
            {
                "key": C.sNew,
                "name": "New",
                "cols": (100, 100, 100)
            }, {
                "key": C.sDraft,
                "name": "Draft",
                "cols": (200, 150, 0)
            }, {
                "key": C.sFinished,
                "name": "Finished",
                "cols": (50, 200, 0)
            }, {
                "key": None,
                "name": "Final",
                "cols": (20, 30, 40)
            }
        ], [
            C.sNote  # Deleted item
        ]
    )

    # Move items, none selected -> no change
    tabStatus.listBox.clearSelection()
    tabStatus._moveItem(1)
    assert [x["key"] for x in tabStatus.getNewList()[0]] == [
        C.sNew, C.sDraft, C.sFinished, None
    ]

    # Move items, first selected, move up -> no change
    tabStatus.listBox.clearSelection()
    tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
    tabStatus._moveItem(-1)
    assert [x["key"] for x in tabStatus.getNewList()[0]] == [
        C.sNew, C.sDraft, C.sFinished, None
    ]

    # Move items, last selected, move up -> allowed
    tabStatus.listBox.clearSelection()
    tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
    tabStatus._moveItem(-1)
    assert [x["key"] for x in tabStatus.getNewList()[0]] == [
        C.sNew, C.sDraft, None, C.sFinished
    ]

    # Move items, same selected, move down -> allowed
    tabStatus._moveItem(1)
    assert [x["key"] for x in tabStatus.getNewList()[0]] == [
        C.sNew, C.sDraft, C.sFinished, None
    ]

    # Importance Tab
    # ==============

    tabImport = projSettings.tabImport
    projSettings._focusTab(GuiProjectSettings.TAB_IMPORT)

    # Delete unused entry
    tabImport.listBox.clearSelection()
    tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(1))
    qtbot.mouseClick(tabImport.delButton, Qt.LeftButton)
    assert tabImport.listBox.topLevelItemCount() == 3

    # Add a new entry
    with monkeypatch.context() as mp:
        mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
        qtbot.mouseClick(tabImport.addButton, Qt.LeftButton)
        tabImport.listBox.clearSelection()
        tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(3))
        for _ in range(8):
            qtbot.keyClick(tabImport.editName, Qt.Key_Backspace, delay=KEY_DELAY)
        for c in "Final":
            qtbot.keyClick(tabImport.editName, c, delay=KEY_DELAY)
        qtbot.mouseClick(tabImport.colButton, Qt.LeftButton)
        qtbot.mouseClick(tabImport.saveButton, Qt.LeftButton)
        assert tabImport.listBox.topLevelItemCount() == 4

    assert tabImport.colChanged is True
    assert tabImport.getNewList() == (
        [
            {
                "key": C.iNew,
                "name": "New",
                "cols": (100, 100, 100)
            }, {
                "key": C.iMajor,
                "name": "Major",
                "cols": (200, 150, 0)
            }, {
                "key": C.iMain,
                "name": "Main",
                "cols": (50, 200, 0)
            }, {
                "key": None,
                "name": "Final",
                "cols": (20, 30, 40)
            }
        ], [
            C.iMinor  # Deleted item
        ]
    )

    # Check Project
    projSettings._doSave()

    statusItems = dict(theProject.data.itemStatus.items())
    assert statusItems[C.sNew]["name"] == "New"
    assert statusItems[C.sDraft]["name"] == "Draft"
    assert statusItems[C.sFinished]["name"] == "Finished"
    assert statusItems["s000013"]["name"] == "Final"

    importItems = dict(theProject.data.itemImport.items())
    assert importItems[C.iNew]["name"] == "New"
    assert importItems[C.iMajor]["name"] == "Major"
    assert importItems[C.iMain]["name"] == "Main"
    assert importItems["i000014"]["name"] == "Final"

    # Clean up
    # qtbot.stop()
    projSettings._doClose()

# END Test testDlgProjSettings_StatusImport


@pytest.mark.gui
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
    """Test the auto-replace tab of the project settings dialog."""
    monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
    monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])

    # Create new project
    mockRnd.reset()
    buildTestProject(nwGUI, projPath)
    CONFIG.setBackupPath(fncPath)

    # Set some values
    theProject = SHARED.project
    theProject.data.setAutoReplace({
        "A": "B", "C": "D"
    })

    # Create Dialog
    projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_REPLACE)
    projSettings.show()
    qtbot.addWidget(projSettings)

    # Auto-Replace Tab
    # ================

    tabReplace = projSettings.tabReplace

    assert tabReplace.listBox.topLevelItem(0).text(0) == "<A>"
    assert tabReplace.listBox.topLevelItem(0).text(1) == "B"
    assert tabReplace.listBox.topLevelItem(1).text(0) == "<C>"
    assert tabReplace.listBox.topLevelItem(1).text(1) == "D"
    assert tabReplace.listBox.topLevelItemCount() == 2

    # Nothing to save or delete
    tabReplace.listBox.clearSelection()
    assert tabReplace._saveEntry() is False
    assert tabReplace._delEntry() is False
    assert tabReplace.listBox.topLevelItemCount() == 2

    # Create a new entry
    qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
    assert tabReplace.listBox.topLevelItemCount() == 3
    assert tabReplace.listBox.topLevelItem(2).text(0) == "<keyword3>"
    assert tabReplace.listBox.topLevelItem(2).text(1) == ""

    # Edit the entry
    tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2))
    tabReplace.editKey.setText("")
    for c in "Th is ":
        qtbot.keyClick(tabReplace.editKey, c, delay=KEY_DELAY)
    tabReplace.editValue.setText("")
    for c in "With This Stuff ":
        qtbot.keyClick(tabReplace.editValue, c, delay=KEY_DELAY)
    qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton)
    assert tabReplace.listBox.topLevelItem(2).text(0) == "<This>"
    assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff "

    # Create a new entry again
    tabReplace.listBox.clearSelection()
    qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
    assert tabReplace.listBox.topLevelItemCount() == 4

    # The list is sorted, so we must find it
    newIdx = -1
    for i in range(tabReplace.listBox.topLevelItemCount()):
        if tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>":
            newIdx = i
            break
    assert newIdx >= 0

    # Then delete the new item
    tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(newIdx))
    qtbot.mouseClick(tabReplace.delButton, Qt.LeftButton)
    assert tabReplace.listBox.topLevelItemCount() == 3

    # Check Project
    projSettings._doSave()
    assert theProject.data.autoReplace == {
        "A": "B", "C": "D", "This": "With This Stuff"
    }

    # Clean up
    # qtbot.stop()
    projSettings._doClose()

# END Test testDlgProjSettings_Replace
