"""specification for handling of metadata METS/MODS DFG-flavour"""

import os
import shutil

from pathlib import Path

import pytest

import lxml.etree as ET

import digiflow as df
import digiflow.common as dfc
import digiflow.validate as dfv

from .conftest import TEST_RES, LIB_RES, exchange_textline

# pylint:disable=c-extension-no-member


def test_metsreader_kitodo2_volume():
    """Got correct primary mods for F-stage?

    Modified with 6x: as F-stage got single parent
    further hierarchy contains 1 entry
    """

    # arrange
    path = os.path.join(TEST_RES, 'k2_mets_vd18_183475917.xml')
    assert os.path.exists(path)
    reader = df.MetsReader(path)

    # act
    mets_report: df.MetsReport = reader.report
    assert mets_report.prime_report
    prime_report: df.DmdReport = mets_report.prime_report

    # assert
    assert not mets_report.files
    assert mets_report.hierarchy == [('183475631', 'multivolume_work')]
    assert prime_report.languages == ['ger']
    assert prime_report.dmd_type == 'Af'
    assert prime_report.identifiers
    assert 'gvk-ppn' in prime_report.identifiers
    assert prime_report.identifiers['gvk-ppn'] == '183475917'
    assert prime_report.locations == 'Nr 83 (6)'
    assert prime_report.licence
    assert len(prime_report.licence) == 2
    assert prime_report.licence[0] == ("use and reproduction",
                                       "http://rightsstatements.org/vocab/InC/1.0/",
                                       "Urheberrechtsschutz 1.0")
    assert prime_report.licence[1] == ("out of print work",
                                       "n.a.",
                                       "Wahrnehmung der Rechte durch die VG WORT (§ 51 VGG)")
    assert prime_report.related == ('host', 'gvk-ppn', '183475631')


def test_metsreader_report_vd18_cstage():
    """
    Are correct informations extracted for c-stage?

    Modified with 6x: as C-stage has no parents
    further hierarchy is empty
    """

    # arrange
    path = os.path.join(TEST_RES, 'migration', '9427342.mets.xml')
    assert os.path.exists(path)
    reader = df.MetsReader(path, 'md9427342')

    # act
    mets_report: df.MetsReport = reader.report
    assert mets_report.prime_report
    prime_report: df.DmdReport = mets_report.prime_report

    # assert
    assert not mets_report.files
    assert mets_report.hierarchy == []
    assert prime_report.dmd_type == 'Ac'
    assert prime_report.languages == ['ger']
    assert prime_report.identifiers
    assert prime_report.identifiers['ulbhalvd18'] == '211999504'
    assert not prime_report.locations


def test_metsreader_report_vd18_fstage():
    """
    Correct informations extracted for f-stage?
    Please note:
        this shelf is located somewhat different =>
        mods:location/mods:holdinSimple/mods:copyInformation/mods:shelfLocator
        This one looks evil, since a shelfLocator is *no* hint for copying ;)

    Modified with 6x: as F-stage got single parent
    further hierarchy contains 1 entry
    """

    # arrange
    path = os.path.join(TEST_RES, 'migration', '9427337.mets.xml')
    assert os.path.exists(path)
    reader = df.MetsReader(path, 'md9427337')

    # act
    mets_report: df.MetsReport = reader.report
    assert mets_report.prime_report
    prime_report: df.DmdReport = mets_report.prime_report

    # assert
    assert not mets_report.files
    assert mets_report.hierarchy == [('9427342', 'multivolume_work')]
    assert prime_report.dmd_type == 'Af'
    assert prime_report.identifiers
    assert prime_report.identifiers['ulbhalvd18'] == '211999628'
    assert prime_report.languages == ['ger']
    assert prime_report.locations == 'Lb 712 a (3,2)'


def test_metsreader_report_k2_goobi_mets():
    """Ensure Kitodo 2 legacy METS to be archived
    is still properly recognized"""

    # tests/resources/mets/k2_meta_10261.xml
    path = os.path.join(TEST_RES, "mets", "k2_meta_10261.xml")
    assert os.path.exists(path)
    reader = df.MetsReader(path)

    # act
    report: df.MetsReport = reader.report

    # assert
    assert report.hierarchy == [('LOG_0002', 'MultiVolumeWork')]
    assert report.prime_report
    prime_report: df.DmdReport = report.prime_report
    assert prime_report.identifiers
    assert prime_report.identifiers["goobi:CatalogSourceID"] == "153142537"
    assert prime_report.identifiers["goobi:anchorID"] == "153142340"
    assert prime_report.identifiers["urn:nbn"] == "urn:nbn:de:gbv:3:1-1192015415-153142537-13"


def test_metsreader_report_vd17_fstage_pica_case():
    """
    Correct informations extracted for VD17 F-stage?
    Is the second letter from PICA preserved?

    Modified with 6x: as F-stage got single parent
    further hierarchy contains 1 entry
    """

    # arrange
    path = os.path.join(TEST_RES,
                        'migration',
                        'vd17-14591176.mets.xml')
    assert os.path.exists(path)
    reader = df.MetsReader(path, 'md14591176')

    # act
    report: df.MetsReport = reader.report
    assert report.prime_report
    prime_report: df.DmdReport = report.prime_report

    # assert
    assert not report.files
    assert report.hierarchy == [('14591136', 'multivolume_work')]
    assert prime_report.dmd_type == 'AF'
    assert prime_report.identifiers
    assert prime_report.identifiers['pon'] == '008499756'
    assert prime_report.languages == ['ger']
    assert prime_report.locations == 'TM0904 (5)'


def test_metsreader_report_hd_monography():
    """
    Correct extractions for hd monography?

    Modified with 6x: Since monograph stands alone,
    further hierarchy is empty
    """

    # arrange
    path = os.path.join(TEST_RES,
                        'vls',
                        'monography',
                        '10595.xml')
    assert os.path.exists(path)
    reader = df.MetsReader(path, 'md10595')

    # act
    report: df.MetsReport = reader.report
    assert report.prime_report
    prime_report: df.DmdReport = report.prime_report

    # assert
    assert not report.files
    assert report.hierarchy == []
    assert prime_report.dmd_type == 'Aa'
    assert prime_report.languages == ['ger']
    assert prime_report.identifiers
    assert prime_report.identifiers['ulbhaldod'] == '187143188'
    assert prime_report.locations
    assert len(prime_report.locations) == 2
    assert prime_report.locations == ['Pon IIg 694, FK', 'Pon IIg 689, 4° (2)']


def test_metsreader_report_kitodo2_export_monography():
    """
    Correct extractions for hd monography?

    Modified with 6x: Since monograph stands alone,
    further hierarchy is empty
    """

    # arrange
    path = os.path.join(TEST_RES, 'k2_mets_vd18_147638674.xml')
    assert os.path.exists(path)
    reader = df.MetsReader(path, 'DMDLOG_0000')

    # act
    report: df.MetsReport = reader.report
    assert report.prime_report
    prime_report: df.DmdReport = report.prime_report
    # need to set this manually since
    # we do not know the kitodo ID from METS
    report.system_identifier = {"kitodo2": "1234"}

    # assert
    assert not report.files
    assert report.system_identifier == {"kitodo2": "1234"}
    assert report.hierarchy == []
    assert prime_report.languages == ['ger']
    assert prime_report.dmd_type == 'Aa'
    assert prime_report.identifiers
    assert prime_report.identifiers['gvk-ppn'] == '147638674'
    assert len(prime_report.identifiers) == 3
    assert prime_report.locations == 'Pon Za 5950, QK'


def test_metsreader_logical_type_1686755_is_document():
    """
    Check digital object 1686755
    * logical type is 'document'
    * no external PICA-type present (=None)

    Modified with 6x: Since document stands alone,
    further hierarchy is empty
    """

    # arrange
    mets = os.path.join(TEST_RES, 'migration/1686755.oai.xml')

    # arrange
    mets_reader = df.MetsReader(mets, 'md1686755')

    # assert
    outcome = mets_reader.inspect_logical_struct()
    assert outcome == ("document", [])


def test_metsreader_type_pica_monography():
    """This item has proper type from pica annotation"""

    # arrange
    mets = os.path.join(TEST_RES, 'migration/201517.oai.xml')

    # act
    mets_reader = df.MetsReader(mets, 'md201517')

    assert "monograph" in mets_reader.inspect_logical_struct()


def test_metsreader_wrong_logical_type():
    """
    What happens when encountered an invalid c-stage oai-response?
    Extended Test due data errors from OCR-D-Pilotproject
    => logical type monograph should be multivolume

    Modified with 6x: Since monograph stands alone,
    further hierarchy is empty
    """

    # arrange
    mets = os.path.join(TEST_RES, 'migration/416811.mets.xml')
    mets_reader = df.MetsReader(mets, 'md416811')

    # has no picaType enriched
    # asserts "monograph", which is actually *wrong*
    # in this case it should be "multivolume_work"
    # f-stage 424336
    # f-stage 415691
    outcome = mets_reader.inspect_logical_struct()
    assert outcome == ('monograph', [])


@pytest.fixture(name="monograph_hd_invalid_physlinks")
def _fixture_monograph_hd_10595(tmpdir):

    target_file = str(tmpdir.mkdir('vlmetadata').join('10595.mets.xml'))
    source_file = os.path.join(TEST_RES, 'migration/10595.mets.xml')
    shutil.copy(source_file, target_file)
    return str(target_file)


def test_metsreader_invalid_physlinks_10595(monograph_hd_invalid_physlinks):
    """
    Handle digital objects with invalid links from
    logical maps to physical structure
    """
    mets = df.MetsReader(monograph_hd_invalid_physlinks, 'md10595')
    structs = mets.get_invalid_physical_structs()

    assert len(structs) == 26
    assert structs[0] == ('log10601', 'phys2376732', 'phys10603')
    assert structs[14] == ('log2376740', 'phys2376742', 'phys10604')
    assert structs[15] == ('log2376737', 'phys2376739', 'phys10604')
    assert structs[16] == ('log2376734', 'phys2376736', 'phys10604')


def test_metsreader_report_for_10595(monograph_hd_invalid_physlinks):
    """Report for digital library object hd/10595"""

    # arrange
    reader = df.MetsReader(monograph_hd_invalid_physlinks, 'md10595')

    # act
    mets_report: df.MetsReport = reader.report
    assert mets_report.prime_report
    report: df.DmdReport = mets_report.prime_report

    # assert
    assert report.dmd_type
    assert 'Aa' in report.dmd_type
    assert report.languages == ['ger']
    assert report.identifiers
    assert report.identifiers['ulbhaldod'] == '187143188'
    assert mets_report.links
    assert len(mets_report.links) == 26


def test_metsreader_logical_type_is_multivolume():
    """
    Check that a multivolume_work with 4 volumes is
    correct recognized as pica 'Ac' and 'multivolume_work'

    Modified with 6x: Since C-Stage stands alone,
    further hierarchy is empty
    """

    # arrange
    mets = os.path.join(TEST_RES, 'migration/9427342.mets.xml')
    mets_reader = df.MetsReader(mets, 'md9427342')

    # act
    _ = mets_reader.report

    # assert
    outcome = mets_reader.inspect_logical_struct()
    assert outcome == ('multivolume_work', [])


def test_metsreader_logical_type_is_tome():
    """
    Check that a part of a multivolume_work, 9427334,
    is VL 'tome' and pica 'Af'

    Modified with 6x: as F-stage got single parent
    further hierarchy contains 1 entry
    """

    # arrange
    mets = os.path.join(TEST_RES, 'migration/9427334.mets.xml')
    mets_reader = df.MetsReader(mets, 'md9427334')

    # act
    _ = mets_reader.report

    # assert
    outcome = mets_reader.inspect_logical_struct()
    assert outcome == ('tome', [('9427342', 'multivolume_work')])


def test_metsreader_ambigious_recordinfo():
    """
    Handle strange cornercase in HD collection where digital objects
    posses several recordInfo elements but first match is empty


    Modified with 6x: Since monograph stands alone,
    further hierarchy is empty
    """

    # arrange
    mets = os.path.join(TEST_RES, 'migration/369765.mets.xml')
    mets_reader = df.MetsReader(mets, 'md369765')

    # act
    _ = mets_reader.report

    # assert
    outcome = mets_reader.inspect_logical_struct()
    assert outcome == ('monograph', [])


def test_metsreader_clear_agents(tmp_path):
    """
    Handle strange cornercase in HD collection where digital objects
    posses several recordInfo elements but th first match is empty
    """

    # arrange
    mets = os.path.join(TEST_RES, 'migration/369765.mets.xml')
    the_orig = ET.parse(mets)  # pyright: ignore[reportCallIssue]
    orig_agents = the_orig.findall('.//mets:agent', dfc.XMLNS)
    assert len(orig_agents) == 4
    dst = tmp_path / '369765.mets.xml'
    shutil.copyfile(mets, str(dst))
    mets_reader = df.MetsReader(str(dst), 'md369765')

    # act
    mets_reader.clear_agents('OTHERTYPE', ['REPOSITORY', 'INSTANCE'])
    result_path = mets_reader.write('ulb')

    # assert
    the_root = ET.parse(str(result_path))  # pyright: ignore[reportCallIssue]
    agents = the_root.findall('.//mets:agent', dfc.XMLNS)
    assert len(agents) == 2
    for agent in agents:
        assert 'REPOSITORY' not in agent.attrib['OTHERTYPE']
        assert 'INSTANCE' not in agent.attrib['OTHERTYPE']


def test_metsreader_enrich_first_agent(tmp_path):
    """
    Ensure mets:agent doesn't yield invalid METS =>
    insert agent at right position
    cf. https://www.loc.gov/standards/mets/mets.xsd

    Prevent
    digiflow.validate.metadata_xsd.InvalidXMLException: 
        [('ERROR', 
          'SCHEMASV', 
          "Element '{http://www.loc.gov/METS/}agent': This element is not expected.")]
    """

    # arrange
    mets = os.path.join(TEST_RES, 'k3_300896638-18490701.xml')
    mets_input = ET.parse(mets)  # pyright: ignore[reportCallIssue]
    orig_agents = mets_input.findall('.//mets:agent', dfc.XMLNS)
    assert len(orig_agents) == 0
    dst = tmp_path / 'mets.xml'
    shutil.copyfile(mets, str(dst))
    mets_reader = df.MetsReader(dst)

    # act
    mets_reader.enrich_agent('Agent Smith')
    result_path = mets_reader.write('ulb')

    # assert
    assert Path(result_path).exists()
    dfv.validate_xml(ET.parse(result_path).getroot())  # pyright: ignore[reportCallIssue]


def test_metsreader_enrich_another_agent(tmp_path):
    """
    Ensure mets:agent doesn't yield invalid METS =>
    insert agent at right position
    cf. https://www.loc.gov/standards/mets/mets.xsd

    Prevent
    digiflow.validate.metadata_xsd.InvalidXMLException: 
        [('ERROR', 
          'SCHEMASV', 
          "Element '{http://www.loc.gov/METS/}agent': This element is not expected.")]
    """

    # arrange
    mets = os.path.join(TEST_RES, 'k3_300896638-18490701.xml')
    mets_input = ET.parse(mets)  # pyright: ignore[reportCallIssue]
    orig_agents = mets_input.findall('.//mets:agent', dfc.XMLNS)
    assert len(orig_agents) == 0
    dst = tmp_path / 'mets.xml'
    shutil.copyfile(mets, str(dst))
    mets_reader = df.MetsReader(dst)

    # act
    mets_reader.enrich_agent('Agent J')
    mets_reader.enrich_agent('Agent K')
    result_path = mets_reader.write('ulb')

    # assert
    assert Path(result_path).exists()
    dfv.validate_xml(ET.parse(result_path).getroot())  # pyright: ignore[reportCallIssue]


def test_metsreader_enrich_agent_kwargs(tmp_path):
    """
    Ensure mets:agent doesn't yield invalid METS =>
    insert agent at right position
    cf. https://www.loc.gov/standards/mets/mets.xsd

    Prevent
    digiflow.validate.metadata_xsd.InvalidXMLException: 
        [('ERROR', 
          'SCHEMASV', 
          "Element '{http://www.loc.gov/METS/}agent': This element is not expected.")]
    """

    # arrange
    mets = os.path.join(TEST_RES, 'k3_300896638-18490701.xml')
    mets_input = ET.parse(mets)  # pyright: ignore[reportCallIssue]
    orig_agents = mets_input.findall('.//mets:agent', dfc.XMLNS)
    assert len(orig_agents) == 0
    dst = tmp_path / '369765.mets.xml'
    shutil.copyfile(mets, str(dst))
    mets_reader = df.MetsReader(dst)

    # act
    kwargs = {'ROLE': 'CREATOR', 'TYPE': 'INDIVIDUAL'}
    mets_reader.enrich_agent(agent_name='Agent Smith', **kwargs)
    result_path = mets_reader.write('ulb')

    # assert
    assert Path(result_path).exists()
    result_root = ET.parse(result_path).getroot()  # pyright: ignore[reportCallIssue]
    dfv.validate_xml(result_root)  # no Exception plz
    assert result_root.xpath('.//mets:agent[@TYPE="INDIVIDUAL"]/mets:name/text()',
                             namespaces=df.XMLNS)[0] == 'Agent Smith'


def test_metsreader_log_hierarchy_menadoc_oai_record_section():
    """
    Handle strange records from menadoc with logical root element
    of type section/article which refers to a MODS section
    without any identifiers from catalogues/databases/urn
    """

    # arrange
    target_file = os.path.join(TEST_RES, 'migration/20586.mets.xml')

    # act
    mreader = df.MetsReader(target_file)
    with pytest.raises(df.DigiflowMetadataException) as rer:
        _ = mreader.report
    assert 'no identifiers' in rer.value.args[0]

    # assert - no, this is not happening!
    # assert _report.identifiers['menadoc.bibliothek.uni-halle.de/dmg'] == '20586'


def test_metsreader_opendata2_inspect_migrated_record_identifiers():
    """Inspect migrated OAI record identifiers
    from digitale.bibliothek.uni-halle.de/vd16 to
    opendata2.uni-halle.de

    Please note: for the first migrated records
    the legacy identifier format was subject to
    change, therefore the mappings must be set
    outside the METS-data
    """

    # arrange
    target_file = os.path.join(TEST_RES, 'mets/vd16_opendata2_1516514412012_4400.xml')
    mets_reader = df.MetsReader(target_file)

    # act
    report: df.MetsReport = mets_reader.report
    assert report.prime_report
    dmd_report: df.DmdReport = report.prime_report

    # assert
    assert mets_reader.dmd_id == 'md998423'
    assert dmd_report.identifiers
    assert len(dmd_report.identifiers) == 5
    assert dmd_report.identifiers == {
        'urn': 'urn:nbn:de:gbv:3:1-507459',
        'vd16': 'ZV 932',
        'bvb': 'VD0034491',
        'gvk-ppn': '567526844',
        'doi': 'doi:10.25673/opendata2-4398'
    }
    assert report.system_identifier == {
        'legacy vlid': '998423',
        'opendata2.uni-halle.de': 'https://opendata2.uni-halle.de//handle/1516514412012/4400',
    }


def test_metsreader_opendata_migrated_record_with_doi():
    """Ensure migrated data finally also contains
    a DOI identifier which was registered afterwards"""

    # arrange
    target_file = os.path.join(TEST_RES, 'opendata/1981185920_43053.xml')
    mets_reader = df.MetsReader(target_file)

    # act
    report: df.MetsReport = mets_reader.report
    assert report.prime_report
    dmd_report: df.DmdReport = report.prime_report

    # assert
    assert mets_reader.dmd_id == 'md1177525'
    assert dmd_report.identifiers
    assert len(dmd_report.identifiers) == 5
    assert dmd_report.identifiers == {
        'urn': 'urn:nbn:de:gbv:3:1-132151',
        'gvk-ppn': '216311322',
        'doi': 'doi:10.25673/41099',
        'gbv': '216311322',
        'vd18': '10078320',
    }
    assert report.system_identifier == {
        'legacy vlid': '1177525',
        'opendata.uni-halle.de': 'https://opendata.uni-halle.de//handle/1981185920/43053',
    }


def test_metsreader_opendata_inspect_migrated_record_origins():
    """How to handle migrate OAI records"""

    # arrange
    target_file = os.path.join(TEST_RES, 'opendata2/vd16-opendata2-1516514412012-4400.xml')
    mets_reader = df.MetsReader(target_file)

    # act
    assert mets_reader.report
    assert mets_reader.report.prime_report
    report: df.DmdReport = mets_reader.report.prime_report

    # without using set get 3 origins, which is unwanted
    assert report.origins
    assert len(report.origins) == 2
    assert report.origins == [("publication", "1574", "Freiberg"),
                              ("digitization", "2013", "Halle (Saale)")]


def test_metsreader_opendata_inspect_publication():
    """Determine behavior with several publication origins"""

    # arrange
    target_file = os.path.join(TEST_RES, 'k2_mets_vd18_147638674.xml')
    mets_reader = df.MetsReader(target_file)

    # act
    assert mets_reader.report
    assert mets_reader.report.prime_report
    report: df.DmdReport = mets_reader.report.prime_report

    # what happened to gnd-link?
    assert report.origins
    assert len(report.origins) == 2
    assert report.origins == [("publication", "1720", "[Dresden](http://d-nb.info/gnd/4012995-0)"),
                              ("digitization", "2021", "Halle (Saale)")]


def test_metsreader_opendata_manipulated_origin(tmp_path):
    """Fix behavior for records with structs like
           <mods:place>
                <mods:placeTerm type="text">London</mods:placeTerm>
            </mods:place>
            <mods:place>
                <mods:placeTerm type="code">XA-GB</mods:placeTerm>
            </mods:place>
    which before yielded no results at all
    """

    # arrange
    target_file = os.path.join(TEST_RES, 'k2_mets_vd18_147638674.xml')
    tmp_path = shutil.copyfile(target_file, os.path.join(tmp_path, '147638674.xml'))
    exchange_textline(tmp_path, {
        '<mods:placeTerm type="text" valueURI="http://d-nb.info/gnd/4012995-0">Dresden</mods:placeTerm>':
        '<mods:placeTerm type="code">XA-GB</mods:placeTerm>'
    })

    # act
    mets_reader = df.MetsReader(tmp_path)
    assert mets_reader.report
    assert mets_reader.report.prime_report
    report: df.DmdReport = mets_reader.report.prime_report
    assert report.origins
    assert len(report.origins) == 2
    assert report.origins == [("publication", "1720", "Dresden"),
                              ("digitization", "2021", "Halle (Saale)")]


# k2_mets_vd18_147638674

def test_metsreader_opendata_inspect_kitodo3_mono_origins():
    """How to handle latest Kitodo 3 DMS export"""

    # arrange
    target_file = os.path.join(TEST_RES, "kitodo3", "1906264740.kxp")
    mets_reader = df.MetsReader(target_file)

    # act
    assert mets_reader.report
    assert mets_reader.report.prime_report
    report: df.DmdReport = mets_reader.report.prime_report

    # 3 origins, which is of course wrong
    assert report.origins
    assert len(report.origins) == 2
    assert report.origins == [("publication", "1560", "[Wien](http://d-nb.info/gnd/4066009-6)"),
                              ("digitization", "2025", "Halle (Saale)")]
    assert report.licence == ('use and reproduction',
                              'https://creativecommons.org/publicdomain/mark/1.0/',
                              'Public Domain Mark 1.0')


def test_metsreader_zd1_issue_16767392():
    """Check METS-Reader-output for digital object
    with logical type issue"""

    # arrange
    mets = os.path.join(TEST_RES, 'vls/zd/zd1-16767392.oai.xml')

    # act
    mets_reader: df.MetsReader = df.MetsReader(mets, 'md16767392')
    mods_reader: df.ModsReader = df.ModsReader(mets_reader.primary_dmd, "md16767392")

    assert "16767392" in mods_reader.get_identifiers()['local']
    the_tree = mets_reader.inspect_logical_struct()
    assert ('issue', [('16602862', 'year'), ('16289662', 'newspaper')]) == the_tree


def test_metsreader_zd1_issue_16359609():
    """Check METS-Reader-output"""

    # arrange
    mets = os.path.join(TEST_RES, 'vls/zd/zd1-16359609.mets.xml')

    # act
    mets_reader: df.MetsReader = df.MetsReader(mets)
    assert mets_reader.report
    assert mets_reader.report.prime_report
    dmd_report: df.DmdReport = mets_reader.report.prime_report

    assert dmd_report.identifiers
    assert "ulbhalvd:16359609" == dmd_report.identifiers['local']
    assert "issue" in mets_reader.inspect_logical_struct()


def test_metsprocessor_clear_filegroups_migration_vd17(tmp_path):
    """Check MetsProcessor"""

    # arrange
    mets = os.path.join(TEST_RES, 'migration/vd17-14591176.mets.xml')
    the_orig = ET.parse(mets)  # pyright: ignore[reportCallIssue]
    orig_file_groups = the_orig.findall('.//mets:fileGrp', dfc.XMLNS)
    assert len(orig_file_groups) == 6

    dst = tmp_path / '14591176.xml'
    shutil.copyfile(mets, str(dst))
    mets_proc = df.MetsProcessor(str(dst), '14591176')

    # act
    mets_proc.clear_filegroups(black_list=['TEASER', 'DOWNLOAD', 'DEFAULT', 'THUMBS', 'MIN'])
    mets_proc.write()
    new_tree = ET.parse(str(dst))  # pyright: ignore[reportCallIssue]
    assert len(new_tree.findall('.//mets:fileGrp', dfc.XMLNS)) == 1
    dfv.validate_xml(new_tree.getroot())


def test_metsprocessor_clear_filegroups_odem_ocrd(tmp_path):
    """MetsProcessor with already ocr-ed VD18 print"""

    # arrange
    mets = os.path.join(TEST_RES, 'opendata/1981185920_38841.xml')
    the_orig = ET.parse(mets)  # pyright: ignore[reportCallIssue]
    orig_file_groups = the_orig.findall('.//mets:fileGrp', dfc.XMLNS)
    assert len(orig_file_groups) == 5

    dst = tmp_path / '1981185920_38841.xml'
    shutil.copyfile(mets, str(dst))
    mets_proc = df.MetsProcessor(str(dst))

    # act
    mets_proc.clear_filegroups(black_list=['DOWNLOAD', 'DEFAULT', 'THUMBS', 'FULLTEXT'])
    mets_proc.write()

    # assert
    new_tree = ET.parse(dst)  # pyright: ignore[reportCallIssue]
    dfv.validate_xml(new_tree.getroot())
    assert len(new_tree.findall('.//mets:fileGrp', dfc.XMLNS)) == 1


def test_metsreader_zd2_issue_18680621():
    """Check METS-Reader with new kitodo3 newspaper structure"""

    # arrange
    mets = os.path.join(TEST_RES, 'kitodo3-zd2/1021634069-18680621.xml')

    # act
    mets_reader: df.MetsReader = df.MetsReader(mets)
    mods_reader: df.ModsReader = df.ModsReader(mets_reader.primary_dmd, mets_reader.dmd_id)

    the_idents = mods_reader.get_identifiers()
    assert the_idents == {
        'urn': 'urn:nbn:de:gbv:3:1-171133730-102163406918680621-11',
        'kxp-ppn': '102163406918680621',
    }
    assert mets_reader.ulb_system_identifier() == {'kitodo3': '4583'}
    log_type, hierarchy = mets_reader.inspect_logical_struct()
    assert mods_reader.get_type() == 'AB'
    assert log_type == 'issue'
    assert hierarchy == [("uuid-b1b11d08-e2d6-45bd-84ef-0b4495013cff", 'day'),
                         ("uuid-479b0e34-6221-4e61-bfa4-4558a185d1bf", 'month'),
                         ('1021634069_1868', 'year'),
                         ('1021634069', 'newspaper')]


@pytest.mark.parametrize(["mets_path", "dmd_id"], [
    (os.path.join(TEST_RES, 'migration/10595.mets.xml'), 'md10595'),
    (os.path.join(TEST_RES, 'migration/9427342.mets.xml'), 'md9427342'),
    (os.path.join(TEST_RES, 'migration/9427337.mets.xml'), 'md9427337'),
    (os.path.join(TEST_RES, 'k2_mets_vd18_058141367.xml'), 'DMDLOG_0000'),
    (os.path.join(TEST_RES, 'k2_mets_vd18_147638674.xml'), 'DMDLOG_0000'),
    (os.path.join(TEST_RES, 'vls/zd/zd1-16359609.mets.xml'), 'md16359609'),
    (os.path.join(TEST_RES, 'opendata/123456789_27949.xml'), 'md1180329'),
])
def test_metsreader_identify_prime_dmd_section(mets_path, dmd_id):
    """Ensure hit proper primaray MODS DMD section"""

    # act
    mets_reader = df.MetsReader(mets_path)

    # assert
    assert mets_reader.dmd_id == dmd_id


def test_metsprocessor_remove_elements_and_close_tags(tmp_path):
    """Ensure: by removing elements, markup like

        <String ID="word1" CONTENT="hellu">
            </String>

        is prohibited
    """

    # arrange
    path_altov4_737429 = os.path.join(TEST_RES, 'ocr', 'alto', 'FULLTEXT_737438.xml')
    dst = tmp_path / '737438.xml'
    shutil.copyfile(path_altov4_737429, str(dst))
    mets_proc = df.MetsProcessor(str(dst))

    # act
    mets_proc.remove(['Shape', 'Processing'])
    path_result = mets_proc.write()

    # assert
    the_lines = [l.strip() for l in open(path_result, encoding='utf-8').readlines()]
    for _line in the_lines:
        assert '</String>' not in _line


def test_metsprocessor_remove_elements_no_keyerror(tmp_path):
    """Ensure legacy namespaces don't cause trouble anymore

    Test Target: Prevent regression
    """

    # arrange
    _a_path = os.path.join(TEST_RES, 'vls_menadoc_99454.mets.xml')
    dst = tmp_path / '99454.xml'
    shutil.copyfile(_a_path, str(dst))
    mets_proc = df.MetsProcessor(str(dst))
    assert len(mets_proc.xpath('//vl:sourceinfo')) == 1

    # act
    mets_proc.remove(['sourceinfo'])
    path_result = mets_proc.write()

    # assert
    resl_proc = df.MetsProcessor(path_result)
    assert len(resl_proc.xpath('//vl:sourceinfo')) == 0


def test_metsreader_logical_hierachy_newspaper_issue():
    """
    Expect the parent hierarchy for single issue
    to be alike this:
    * newspaper 16289661
    * year 17308997
    * month 03
    * day 0322
    * issue 16359603
    """

    # arrange
    mets = os.path.join(TEST_RES, 'vls/zd/zd1-issue-16359603.zmets.xml')
    mets_reader = df.MetsReader(mets, 'md16359603')

    # act
    log_typ, hierachy = mets_reader.inspect_logical_struct()

    # assert
    assert log_typ == 'issue'
    assert hierachy == [('day0322', 'day'), ('month03', 'month'),
                        ('17308997', 'year'), ('16289661', 'newspaper')]


def test_metsreader_missing_struct_mapping():
    """Check behavior if METS contains logical containers
    without corresponding physical stuff - i.e. an empty
    logical section without any pages which will 
    kill Derivans 1.6.4 afterwards

    => filter em out
    """

    # arrange
    mets = os.path.join(TEST_RES, 'opendata/1981185920_43053.xml')
    mets_reader = df.MetsReader(mets)
    assert mets_reader.dmd_id == 'md1177525'

    # act
    with pytest.raises(df.DigiflowMetadataException) as _runtime_error:
        mets_reader.inspect_logical_struct_links()

    # assert
    assert " no link for logical section:'log1646693'" in _runtime_error.value.args[0]


DIGIFLOW_CONFIG = LIB_RES / 'digilife.ini'


def test_metsreader3_report_vd18_cstage():
    """
    Are correct informations extracted for c-stage?
    * no more further METS hierarchy
    * no related-something in primary MODS
    """

    # arrange
    path = os.path.join(TEST_RES, 'migration', '9427342.mets.xml')
    assert os.path.exists(path)
    reader = df.MetsReader(path, 'md9427342')

    # act
    report = reader.report
    assert report.prime_report
    dmd_report: df.DmdReport = report.prime_report

    # assert
    assert not report.files
    assert report.system_identifier == {'digitale.bibliothek.uni-halle.de/vd18': '9427342'}
    assert report.hierarchy == []
    assert dmd_report.languages == ['ger']
    assert dmd_report.identifiers == {'gbv': '211999504',
                                      'ulbhalvd18': '211999504',
                                      'urn': 'urn:nbn:de:gbv:3:1-636051',
                                      'vd18': '11228628'}
    assert dmd_report.dmd_type == 'Ac'
    assert not dmd_report.locations
    assert dmd_report.related == []


def test_metsreader3_report_vd18_fstage():
    """
    Correct informations extracted for f-stage?

    Modified with 6x: as F-stage got single parent
    further hierarchy contains 1 entry
    """

    # arrange
    path = os.path.join(TEST_RES, 'migration', '9427337.mets.xml')
    assert os.path.exists(path)
    reader = df.MetsReader(path, 'md9427337')

    # act
    mets_report = reader.report
    assert mets_report.prime_report
    dmd_report: df.DmdReport = mets_report.prime_report

    # assert
    assert dmd_report.languages == ['ger']
    assert not mets_report.files
    assert mets_report.logical_type == "volume"
    assert mets_report.hierarchy == [('9427342', 'multivolume_work')]
    assert mets_report.system_identifier == {'digitale.bibliothek.uni-halle.de/vd18': '9427337'}
    assert dmd_report.identifiers == {'gbv': '211999628',
                                      'ulbhalvd18': '211999628',
                                      'urn': 'urn:nbn:de:gbv:3:1-635986',
                                      'vd18': '90311817'}
    assert dmd_report.locations == 'Lb 712 a (3,2)'


def test_metsreader_kitodo2_mena_periodical_volume_dmd_id():
    """Process rather unbalanced Kitodo2 MENA volume
    Metadata, i.e. we failed to recognize the prime
    MODS section at first sight
    """

    # arrange
    mets = os.path.join(TEST_RES, 'k2_mets_mena_12988274719564.xml')

    # act
    mets_reader = df.MetsReader(mets)

    # assert
    assert mets_reader.dmd_id == 'DMDLOG_0001'


def test_metadata_processor_contains_single_fgroup():
    """Ensure behavior for passing single str arg
    Bugfix: 
    TypeError: '>' not supported between instances of 'list' and 'int'
    """

    _proc = df.MetsProcessor(TEST_RES / 'k2_mets_morbio_1748529021.xml')
    assert _proc.contains_group('MAX')


def test_metadata_processor_contains_multiple_fgroup():
    """Ensure behavior for passing list args"""

    _proc = df.MetsProcessor(TEST_RES / 'k2_mets_morbio_1748529021.xml')
    assert _proc.contains_group(['MAX'])


def test_mets_reader_some_sbb_mets():
    """Explore SBB METS/MODS"""

    the_reader = df.MetsReader(TEST_RES / "mets" / "SBB_PPN1000056597.xml")
    the_report = the_reader.report
    assert the_report.prime_report
    dmd_report: df.DmdReport = the_report.prime_report
    assert the_report.logical_type == "monograph"
    assert dmd_report.languages == ["ger"]


def test_mets_reader_newspaper_year_1921():
    """Behavior if year METS encountered"""

    the_reader = df.MetsReader(TEST_RES / "mets" / "newspaper" /
                               "1516514412012_175735_year_1921.xml")
    the_report = the_reader.report
    assert the_report.prime_report
    dmd_report: df.DmdReport = the_report.prime_report
    assert the_report.logical_type == "year"
    assert dmd_report.languages == ["ger"]


def test_metsreader_kitodo2_058141367():
    """Prevent undesired behavior: subsequent
    calls created ever more dmd_reports!
    """

    # arrange
    mets = TEST_RES / "k2-mets-058141367.k2x"
    assert mets.is_file()

    # act
    mets_reader_01 = df.MetsReader(mets)
    assert mets_reader_01.primary_dmd is not None
    mods_idents = mets_reader_01.primary_dmd.xpath(
        'mods:recordInfo/mods:recordIdentifier/text()',
        namespaces=df.XMLNS)

    # assert
    assert mods_idents[0] == "058141367"
    assert mets_reader_01.report
    assert mets_reader_01.report.dmd_reports
    assert len(mets_reader_01.report.dmd_reports) == 1

    # re-act
    _ = df.MetsReader(mets).report  # thouse created next dmd_report entry
    mets_reader_02 = df.MetsReader(mets)
    assert mets_reader_02.report
    assert mets_reader_02.report.dmd_reports
    assert len(mets_reader_02.report.dmd_reports) == 1


def test_metsreader_dataclass():
    """Ensure unset attribute exists and is
    initialized with 'None' using Python 3.10+
    and prevent ... object has no attribute ... AttributeErrors
    """

    report = df.MetsReport()
    assert report.system_identifier is None


TEST_XML_AS_STRING = '''
<root>
    <child>test xmlprocessing to initialize object text1</child>
    <child>test xmlprocessing to initialize object text2</child>
    <child>test xmlprocessing to initialize object text3</child>
    <child>test xmlprocessing to initialize object text4</child>
    <child>test xmlprocessing to initialize object text5</child>
</root>
'''


@pytest.mark.parametrize("input_xml", [
    (ET.fromstring('<root><child>text</child></root>')),
    (TEST_RES / "mets" / "k2_meta_10261.xml"),
    (TEST_XML_AS_STRING),
    (bytes(TEST_XML_AS_STRING, encoding='utf-8'))
])
def test_xmlprocessor_init_with(input_xml):
    """Test XMLProcessor initialization with various input types
    Ensure correct root element is set, which is 'root' in
    case of the above artificial test items or 'mets' for METS files
    """
    # arrange
    proc = None

    # act
    proc = df.XMLProcessor(input_xml)

    # assert
    assert proc is not None
    assert proc.root is not None
    assert proc.root.tag in ['{http://www.loc.gov/METS/}mets', 'root']


@pytest.mark.parametrize("input_xml", [
    (Path('/not/existing/file.xml')),
    (123456),
    (None)
])
def test_xmlprocessor_init_with_invalid_input(input_xml):
    """Test XMLProcessor raises exception for invalid input data"""
    with pytest.raises(df.DigiflowMetadataException):
        df.XMLProcessor(input_xml)
