from __future__ import annotations

from xml.dom import minidom

from tortoisehg.hgqt.qtcore import (
    pyqtSignal,
    QByteArray,
    QBuffer,
    QModelIndex,
    QObject,
    QIODevice,
)

from mercurial import node

from tortoisehg.hgqt import (
    repotreemodel,
    repotreeitem,
)

def prettyxml(s):
    dom = minidom.parseString(s)
    return dom.toprettyxml(indent='  ').splitlines()

def with_qbuffer(data=b'', mode=QIODevice.OpenModeFlag.ReadOnly):
    def decorate(func):
        def newfunc():
            ba = QByteArray(data)  # keep reference to avoid GC
            f = QBuffer(ba)
            f.open(mode)
            try:
                func(f)
            finally:
                f.close()
        newfunc.__name__ = func.__name__
        return newfunc
    return decorate

full_data = br'''<?xml version="1.0" encoding="UTF-8"?>
<reporegistry>
  <treeitem>
    <allgroup name="default">
      <repo root="/thg" shortname="thg"
            basenode="bac32db38e52fd49acb62b94730a55f4f4b0cdee"/>
      <repo root="/mercurial" shortname="hg"
            basenode="9117c6561b0bd7792fa13b50d28239d51b78e51f"
            sharedpath="/mirror/mercurial/.hg"/>
      <subrepo root="/subroot/sub" shortname="sub"
               basenode="2f425e331c8cdffa5103f3b181358092245bdc10"/>
    </allgroup>
    <group name="bar">
      <group name="baz">
        <repo root="/qux" shortname="qux"
              basenode="6c30f00cc82daff63b1260eec198256a9c8e5a56"/>
      </group>
      <repo root="/subroot" shortname="subroot"
            basenode="b986218ba1c9b0d6a259fac9b050b1724ed8e545">
        <subrepo root="/subroot/svnsub" repotype="svn"/>
        <subrepo root="/subroot/sub" shortname="sub"
                 basenode="2f425e331c8cdffa5103f3b181358092245bdc10"/>
      </repo>
    </group>
  </treeitem>
</reporegistry>
'''

full_data_standalone_repos = ['/thg', '/mercurial', '/subroot/sub',
                              '/qux', '/subroot']
full_data_all_repos = (full_data_standalone_repos
                       + ['/subroot/svnsub', '/subroot/sub'])

@with_qbuffer(full_data, QIODevice.OpenModeFlag.ReadWrite)
def test_readwritexml(f):
    root = repotreemodel.readXml(f, 'reporegistry')
    f.buffer().clear()
    f.reset()
    repotreemodel.writeXml(f, root, 'reporegistry')
    assert prettyxml(bytes(f.data())) == prettyxml(full_data)

@with_qbuffer(full_data)
def test_iterrepoitemfromxml(f):
    repos = list(repotreemodel.iterRepoItemFromXml(f))
    assert [e.rootpath() for e in repos] == full_data_standalone_repos
    assert repos[0].shortname() == 'thg'
    assert (node.hex(repos[0].basenode())
            == b'bac32db38e52fd49acb62b94730a55f4f4b0cdee')

@with_qbuffer(full_data)
def test_getrepoitemlist_all(f):
    root = repotreemodel.readXml(f, 'reporegistry')
    items = repotreemodel.getRepoItemList(root)
    assert [e.rootpath() for e in items] == full_data_all_repos

@with_qbuffer(full_data)
def test_getrepoitemlist_standalone(f):
    root = repotreemodel.readXml(f, 'reporegistry')
    items = repotreemodel.getRepoItemList(root, standalone=True)
    assert [e.rootpath() for e in items] == full_data_standalone_repos

subrepos_data = br'''<?xml version="1.0" encoding="UTF-8"?>
<reporegistry>
  <treeitem>
    <allgroup name="default">
      <subrepo root="/subroot/sub" shortname="sub"
               basenode="2f425e331c8cdffa5103f3b181358092245bdc10"/>
      <repo root="/subroot" shortname="subroot"
            basenode="b986218ba1c9b0d6a259fac9b050b1724ed8e545">
        <subrepo root="/subroot/svnsub" repotype="svn"/>
        <subrepo root="/subroot/sub" shortname="sub"
                 basenode="2f425e331c8cdffa5103f3b181358092245bdc10"/>
      </repo>
    </allgroup>
  </treeitem>
</reporegistry>
'''

@with_qbuffer(subrepos_data)
def test_undumpsubrepos(f):
    """<subrepo> element should be mapped to different classes"""
    root = repotreemodel.readXml(f, 'reporegistry')
    allgroup = root.child(0)
    assert type(allgroup.child(0)) is repotreeitem.StandaloneSubrepoItem, \
        repr(allgroup.child(0))
    subroot = allgroup.child(1)
    assert type(subroot) is repotreeitem.RepoItem, repr(subroot)
    assert type(subroot.child(0)) is repotreeitem.AlienSubrepoItem, \
        repr(subroot.child(0))
    assert type(subroot.child(1)) is repotreeitem.SubrepoItem, \
        repr(subroot.child(1))

filter_data = br'''<?xml version="1.0" encoding="UTF-8"?>
<reporegistry>
  <treeitem>
    <allgroup name="default">
      <repo root="/standalone" shortname="standalone" basenode="6c30f00cc82daff63b1260eec198256a9c8e5a56"/>
      <repo root="/subroot" shortname="subroot" basenode="b986218ba1c9b0d6a259fac9b050b1724ed8e545">
        <subrepo root="/subroot/svnsub" repotype="svn" shortname="svn"/>
        <subrepo root="/subroot/sub" shortname="sub" basenode="2f425e331c8cdffa5103f3b181358092245bdc10"/>
      </repo>
    </allgroup>
    <group name="fooGroup">
      <group name="barGroup">
        <repo root="/subsubroot" shortname="subsubroot2" basenode="b986218ba1c9b0d6a259fac9b050b1724ed8e545">
          <subrepo root="/subsubroot/subroot2" shortname="subroot2" basenode="b986218ba1c9b0d6a259fac9b050b1724ed8e545">
            <subrepo root="/subsubroot/subroot2/svnsub2" repotype="svn"/>
            <subrepo root="/subsubroot/subroot2/sub2" shortname="sub2" basenode="2f425e331c8cdffa5103f3b181358092245bdc10"/>
          </subrepo>
        </repo>
      </group>
    </group>
  </treeitem>
</reporegistry>
'''

class DummyRepoManager(QObject):
    configChanged = pyqtSignal(str)
    repositoryChanged = pyqtSignal(str)
    repositoryOpened = pyqtSignal(str)

@with_qbuffer(filter_data)
def test_repotreemodel(f):
    """﻿Test data used for the filtering tests"""
    repomanager = DummyRepoManager()
    model = repotreemodel.RepoTreeModel(f, repomanager)

    rootIndex = QModelIndex()
    assert model.rowCount(rootIndex) == 2

    assert model.data(model.index(0, 1, rootIndex)) == ''
    allGroupIndex = model.index(0, 0, rootIndex)
    assert model.data(allGroupIndex) == 'default'
    assert model.rowCount(allGroupIndex) == 2
    assert model.data(model.index(0, 0, allGroupIndex)) == 'standalone'
    assert model.data(model.index(0, 1, allGroupIndex)) == '/standalone'
    assert model.data(model.index(1, 1, allGroupIndex)) == '/subroot'
    subrootIndex = model.index(1, 0, allGroupIndex)
    assert model.data(subrootIndex) == 'subroot'
    assert model.rowCount(subrootIndex) == 2
    assert model.data(model.index(0, 0, subrootIndex)) == 'svnsub'
    assert model.data(model.index(0, 1, subrootIndex)) == '/subroot/svnsub'
    assert model.data(model.index(1, 0, subrootIndex)) == 'sub'
    assert model.data(model.index(1, 1, subrootIndex)) == '/subroot/sub'

    assert model.data(model.index(1, 1, rootIndex)) == ''
    fooGroupIndex = model.index(1, 0, rootIndex)
    assert model.data(fooGroupIndex) == 'fooGroup'
    assert model.rowCount(fooGroupIndex) == 1
    assert model.data(model.index(0, 1, fooGroupIndex)) == ''
    barGroupIndex = model.index(0, 0, fooGroupIndex)
    assert model.data(barGroupIndex) == 'barGroup'
    assert model.rowCount(barGroupIndex) == 1
    assert model.data(model.index(0, 1, barGroupIndex)) == '/subsubroot'
    subsubroot2Index = model.index(0, 0, barGroupIndex)
    assert model.data(subsubroot2Index) == 'subsubroot2'
    assert model.rowCount(subsubroot2Index) == 1
    assert model.data(model.index(0, 1, subsubroot2Index)) == '/subsubroot/subroot2'
    subroot2Index = model.index(0, 0, subsubroot2Index)
    assert model.data(subroot2Index) == 'subroot2'
    assert model.rowCount(subroot2Index) == 2
    assert model.data(model.index(0, 0, subroot2Index)) == 'svnsub2'
    assert model.data(model.index(0, 1, subroot2Index)) == '/subsubroot/subroot2/svnsub2'
    assert model.data(model.index(1, 0, subroot2Index)) == 'sub2'
    assert model.data(model.index(1, 1, subroot2Index)) == '/subsubroot/subroot2/sub2'
