import pytest

from pytestqt.qt_compat import qt_api
from pytestqt import modeltest

pytestmark = pytest.mark.usefixtures("qtbot")


class BasicModel(qt_api.QtCore.QAbstractItemModel):
    def data(self, index, role=qt_api.QtCore.Qt.DisplayRole):
        return None

    def rowCount(self, parent=qt_api.QtCore.QModelIndex()):
        return 0

    def columnCount(self, parent=qt_api.QtCore.QModelIndex()):
        return 0

    def index(self, row, column, parent=qt_api.QtCore.QModelIndex()):
        return qt_api.QtCore.QModelIndex()

    def parent(self, index):
        return qt_api.QtCore.QModelIndex()


def test_standard_item_model(qtmodeltester):
    """
    Basic test which uses qtmodeltester with a qt_api.QStandardItemModel.
    """
    model = qt_api.QStandardItemModel()
    items = [qt_api.QStandardItem(str(i)) for i in range(6)]
    model.setItem(0, 0, items[0])
    model.setItem(0, 1, items[1])
    model.setItem(1, 0, items[2])
    model.setItem(1, 1, items[3])

    items[0].setChild(0, items[4])
    items[4].setChild(0, items[5])

    qtmodeltester.check(model, force_py=True)


def test_string_list_model(qtmodeltester):
    model = qt_api.QStringListModel()
    model.setStringList(["hello", "world"])
    qtmodeltester.check(model, force_py=True)


def test_sort_filter_proxy_model(qtmodeltester):
    model = qt_api.QStringListModel()
    model.setStringList(["hello", "world"])
    proxy = qt_api.QSortFilterProxyModel()
    proxy.setSourceModel(model)
    qtmodeltester.check(proxy, force_py=True)


@pytest.mark.parametrize(
    "broken_role",
    [
        qt_api.QtCore.Qt.ToolTipRole,
        qt_api.QtCore.Qt.StatusTipRole,
        qt_api.QtCore.Qt.WhatsThisRole,
        qt_api.QtCore.Qt.SizeHintRole,
        qt_api.QtCore.Qt.FontRole,
        qt_api.QtCore.Qt.BackgroundColorRole,
        qt_api.QtCore.Qt.TextColorRole,
        qt_api.QtCore.Qt.TextAlignmentRole,
        qt_api.QtCore.Qt.CheckStateRole,
    ],
)
def test_broken_types(check_model, broken_role):
    """
    Check that qtmodeltester correctly captures data() returning invalid
    values for various display roles.
    """

    class BrokenTypeModel(qt_api.QAbstractListModel):
        def rowCount(self, parent=qt_api.QtCore.QModelIndex()):
            if parent == qt_api.QtCore.QModelIndex():
                return 1
            else:
                return 0

        def data(
            self, index=qt_api.QtCore.QModelIndex(), role=qt_api.QtCore.Qt.DisplayRole
        ):
            if role == broken_role:
                return object()  # This will fail the type check for any role
            else:
                return None

    check_model(BrokenTypeModel(), should_pass=False)


@pytest.mark.parametrize(
    "role_value, should_pass",
    [
        (qt_api.QtCore.Qt.AlignLeft, True),
        (qt_api.QtCore.Qt.AlignRight, True),
        (0xFFFFFF, False),
        ("foo", False),
        (object(), False),
    ],
)
def test_data_alignment(role_value, should_pass, check_model):
    """Test a custom model which returns a good and alignments from data().
    qtmodeltest should capture this problem and fail when that happens.
    """

    class MyModel(qt_api.QAbstractListModel):
        def rowCount(self, parent=qt_api.QtCore.QModelIndex()):
            return 1 if parent == qt_api.QtCore.QModelIndex() else 0

        def data(
            self, index=qt_api.QtCore.QModelIndex(), role=qt_api.QtCore.Qt.DisplayRole
        ):
            if role == qt_api.QtCore.Qt.TextAlignmentRole:
                return role_value
            elif role == qt_api.QtCore.Qt.DisplayRole:
                if index == self.index(0, 0):
                    return "Hello"
            return None

    check_model(MyModel(), should_pass=should_pass)


def test_header_handling(check_model):
    class MyModel(qt_api.QAbstractListModel):
        def rowCount(self, parent=qt_api.QtCore.QModelIndex()):
            return 1 if parent == qt_api.QtCore.QModelIndex() else 0

        def set_header_text(self, header):
            self._header_text = header
            self.headerDataChanged.emit(qt_api.QtCore.Qt.Vertical, 0, 0)
            self.headerDataChanged.emit(qt_api.QtCore.Qt.Horizontal, 0, 0)

        def headerData(self, section, orientation, role=qt_api.QtCore.Qt.DisplayRole):
            return self._header_text

        def data(
            self, index=qt_api.QtCore.QModelIndex(), role=qt_api.QtCore.Qt.DisplayRole