from lxml import etree
from dateutil.parser import isoparse
from calendar import monthrange
import numpy as np
import pandas as pd
import os
from functools import partial

attr_qname = etree.QName('http://www.w3.org/2001/XMLSchema-instance',
                         'schemaLocation')
nsmap = {
    None: 'http://www.loc.gov/MARC21/slim',
    'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
}


def create_skeleton():
    """Create a base template for the MARC-21 XML file. This file will
    be uploaded to the catalog.

    :returns: XML object tree of template.
    """
    root = etree.Element('collection', {
        attr_qname:
        'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd'
    },
                         nsmap=nsmap)
    return root


def create_record(root, row):
    record = etree.SubElement(root, 'record')
    # Add common fields
    leader = etree.SubElement(record, 'leader')
    leader.text = '00000naa#a2200000zc#4500'
    ctrl005 = etree.SubElement(record, 'controlfield', {'tag': '005'})
    ctrl005.text = '20240101080000.0'
    ctrl007 = etree.SubElement(record, 'controlfield', {'tag': '007'})
    ctrl007.text = 'tu'

    record = create_dato(record, row.Dato, row.Lingvo)
    record = create_verkinto(record, row.Verkinto)
    record = create_revuo(record, row.Revuo)
    record = create_titolo(record, row.Titolo)
    record = create_rubriko(record, row.Rubriko)
    record = create_loko(record, row.Loko)
    record = create_sercvortoj(record, row.Serĉvortoj)
    record = create_lingvo(record, row.Lingvo)
    record = create_links(record, row.Links)
    record = create_ac_mappe(record, row._11, row.Mappe)
    record = create_common(record)
    return root


def create_revuo(record, revuo):
    if revuo == '' or pd.isnull(revuo):
        return record
    field490 = etree.SubElement(record, 'datafield', {
        'tag': '490',
        'ind1': '0',
        'ind2': ' '
    })
    field490_a = etree.SubElement(field490, 'subfield', {'code': 'a'})
    field490_a.text = revuo
    return record


def create_verkinto(record, verkinto):
    if verkinto == '' or pd.isnull(verkinto):
        return record
    field100 = etree.SubElement(record, 'datafield', {
        'tag': '100',
        'ind1': '1',
        'ind2': ' '
    })
    field100_a = etree.SubElement(field100, 'subfield', {'code': 'a'})
    field100_a.text = verkinto
    field100_4 = etree.SubElement(field100, 'subfield', {'code': '4'})
    field100_4.text = 'aut'
    return record


def create_dato(record, dato, lingvo):
    date_parsing = True
    try:
        dates = dato.replace(',', '/').split('/')
        dates_obj = [isoparse(dato) for dato in dates]
        dates_str = [dato.strftime('%d.%m.%Y') for dato in dates_obj]
    except:
        print(f'Something happened during parsing of dates: {dato}')
        date_parsing = False
    if lingvo == '' or pd.isnull(lingvo) or len(lingvo[0]) > 3:
        langs = ['###']
    else:
        langs = lingvo.split(',')
    ctrl008 = etree.SubElement(record, 'controlfield', {'tag': '008'})
    if date_parsing:
        ctrl008.text = f'240101|{dates_obj[0].year}####xx############|||#|#{langs[0]}#c'
    else:
        ctrl008.text = f'240101|########xx############|||#|#{langs[0]}#c'
        return record

    if len(dates_obj) == 1:
        date = dates_obj[0]
        date_str = dates_str[0]
        if len(dates[0]) == 10:
            # Date has year, month, day
            # Datafield with tag 046 as DD.MM.YYYY
            field046 = etree.SubElement(record, 'datafield', {
                'tag': '046',
                'ind1': ' ',
                'ind2': ' '
            })
            field046_k = etree.SubElement(field046, 'subfield', {'code': 'k'})
            field046_k.text = date_str
            # Datafield with tag 264 as DD.MM.YYYY
            field264 = etree.SubElement(record, 'datafield', {
                'tag': '264',
                'ind1': ' ',
                'ind2': ' '
            })
            field264_c = etree.SubElement(field264, 'subfield', {'code': 'c'})
            field264_c.text = date_str
        if len(dates[0]) == 7:
            # Date only has year and month
            # Datafield with tag 046 as 30/31.MM.YYYY
            field046 = etree.SubElement(record, 'datafield', {
                'tag': '046',
                'ind1': ' ',
                'ind2': ' '
            })
            field046_k = etree.SubElement(field046, 'subfield', {'code': 'k'})
            field046_k.text = date_str
            field046_l = etree.SubElement(field046, 'subfield', {'code': 'l'})
            field046_l.text = str(monthrange(
                date.year, date.month)[1]) + '.' + date_str[3:]
            # Datafield with tag 264 as ??.MM.YYYY
            field264 = etree.SubElement(record, 'datafield', {
                'tag': '264',
                'ind1': ' ',
                'ind2': ' '
            })
            field264_c = etree.SubElement(field264, 'subfield', {'code': 'c'})
            field264_c.text = '??.' + date_str[3:]
        elif len(dates[0]) == 4:
            # Date only has year
            field264 = etree.SubElement(record, 'datafield', {
                'tag': '264',
                'ind1': ' ',
                'ind2': ' '
            })
            field264_c = etree.SubElement(field264, 'subfield', {'code': 'c'})
            field264_c.text = str(date.year)
        else:
            # Date is nan or malformed
            pass
    else:
        # Date is range DD-MM-YYYY/DD-MM-YYYY (len at most two)
        first_date_obj = dates_obj[0]
        second_date_obj = dates_obj[1]
        first_date_str = dates_str[0]
        second_date_str = dates_str[1]
        if len(dates[0]) == 10 and len(dates[1]) == 10:
            # Both dates have year, month, day
            # Datafield with tag 046
            field046 = etree.SubElement(record, 'datafield', {
                'tag': '046',
                'ind1': ' ',
                'ind2': ' '
            })
            field046_k = etree.SubElement(field046, 'subfield', {'code': 'k'})
            field046_k.text = first_date_str
            field046_l = etree.SubElement(field046, 'subfield', {'code': 'l'})
            field046_l.text = second_date_str
            # Datafield with tag 264 as DD.MM.YYYY-DD.MM.YYYY
            field264 = etree.SubElement(record, 'datafield', {
                'tag': '264',
                'ind1': ' ',
                'ind2': ' '
            })
            field264_c = etree.SubElement(field264, 'subfield', {'code': 'c'})
            field264_c.text = first_date_str + '-' + second_date_str
        elif len(dates[0]) == 7 and len(dates[1]) == 7:
            # Both dates have year, month
            field046 = etree.SubElement(record, 'datafield', {
                'tag': '046',
                'ind1': ' ',
                'ind2': ' '
            })
            field046_k = etree.SubElement(field046, 'subfield', {'code': 'k'})
            field046_k.text = first_date_str
            field046_l = etree.SubElement(field046, 'subfield', {'code': 'l'})
            field046_l.text = str(
                monthrange(
                    second_date_obj.year,
                    second_date_obj.month)[1]) + '.' + second_date_str[3:]
            # Datafield with tag 264 as DD.MM.YYYY-DD.MM.YYYY
            field264 = etree.SubElement(record, 'datafield', {
                'tag': '264',
                'ind1': ' ',
                'ind2': ' '
            })
            field264_c = etree.SubElement(field264, 'subfield', {'code': 'c'})
            field264_c.text = '??.' + first_date_str[
                3:] + '-' + '??.' + second_date_str[3:]
        else:
            # Date is nan or malformed
            pass

    return record


def create_titolo(record, titolo):
    if titolo == '' or pd.isnull(titolo):
        print(f'Titolo {titolo} is null')
        print(f'CANNOT CREATE RECORD IF TITOLO NULL')
        return record

    titolo = titolo.split(':', maxsplit=1)
    titolo = [elem.strip() for elem in titolo]
    field245 = etree.SubElement(record, 'datafield', {
        'tag': '245',
        'ind1': '1',
        'ind2': '0'
    })
    field245_a = etree.SubElement(field245, 'subfield', {'code': 'a'})
    field245_a.text = titolo[0]
    if len(titolo) == 2:
        field245_b = etree.SubElement(field245, 'subfield', {'code': 'b'})
        field245_b.text = titolo[1]
    if len(titolo) > 2:
        print(f'There is more than one : in the title: {titolo}')

    return record


def create_rubriko(record, rubriko):
    if rubriko == '' or pd.isnull(rubriko):
        return record
    field500 = etree.SubElement(record, 'datafield', {
        'tag': '500',
        'ind1': ' ',
        'ind2': ' '
    })
    field500_a = etree.SubElement(field500, 'subfield', {'code': 'a'})
    field500_a.text = 'Unter der Rubrik: ' + rubriko
    return record


def create_loko(record, loko):
    if loko == '' or pd.isnull(loko):
        return record
    field264 = record.find("./datafield[@tag='264']")
    if field264 is not None:
        # Datafield with tag 264 already written by Dato
        field264_a = etree.SubElement(field264, 'subfield', {'code': 'a'})
        field264_a.text = loko
    else:
        field264 = etree.SubElement(record, 'datafield', {
            'tag': '264',
            'ind1': ' ',
            'ind2': '0'
        })
        field264_a = etree.SubElement(field264, 'subfield', {'code': 'a'})
        field264_a.text = loko
    return record


def create_sercvortoj(record, sercvortoj):
    if sercvortoj == '' or pd.isnull(sercvortoj):
        return record
    sercvortoj = sercvortoj.split(';')
    sercvortoj = [elem.strip() for elem in sercvortoj]
    for elem in sercvortoj:
        field653 = etree.SubElement(record, 'datafield', {
            'tag': '653',
            'ind1': ' ',
            'ind2': ' '
        })
        field653_a = etree.SubElement(field653, 'subfield', {'code': 'a'})
        field653_a.text = elem
    return record


def create_lingvo(record, lingvo):
    if lingvo == '' or pd.isnull(lingvo):
        return record
    lingvo = lingvo.split(',')
    lingvo = [elem.strip() for elem in lingvo]
    for elem in lingvo:
        if len(elem) > 3:
            continue
        field041 = etree.SubElement(record, 'datafield', {
            'tag': '041',
            'ind1': ' ',
            'ind2': ' '
        })
        field041_a = etree.SubElement(field041, 'subfield', {'code': 'a'})
        field041_a.text = elem
    return record


def create_links(record, links):
    if links == '' or pd.isnull(links):
        return record
    links = links.split(',')
    # First link to datafield 856 subfield u
    field856 = etree.SubElement(record, 'datafield', {
        'tag': '856',
        'ind1': '4',
        'ind2': '2'
    })
    field856_u = etree.SubElement(field856, 'subfield', {'code': 'u'})
    field856_u.text = links[0]
    # Common to subfields m, q, x, 3, o
    field856_m = etree.SubElement(field856, 'subfield', {'code': 'm'})
    field856_m.text = 'B:AT-OeNB'
    field856_q = etree.SubElement(field856, 'subfield', {'code': 'q'})
    field856_q.text = 'text/html'
    field856_x = etree.SubElement(field856, 'subfield', {'code': 'x'})
    field856_x.text = 'ONB-DTL'
    field856_3 = etree.SubElement(field856, 'subfield', {'code': '3'})
    field856_3.text = 'Digitales Objekt'
    field856_o = etree.SubElement(field856, 'subfield', {'code': 'o'})
    field856_o.text = 'OBV-ONB-DTL'
    # Write number of links to datafield 300 subfield a
    field300 = etree.SubElement(record, 'datafield', {
        'tag': '300',
        'ind1': ' ',
        'ind2': ' '
    })
    field300_a = etree.SubElement(field300, 'subfield', {'code': 'a'})
    field300_a.text = str(len(links)) + ' Blatt'
    return record


def create_ac_mappe(record, ac, mappe):
    if ac == '' or pd.isnull(ac):
        print('AC-Number cannot be empty or null!')
        return record
    # AC to datafield 773 subfield w with prefix (AT-OBV)
    field773 = etree.SubElement(record, 'datafield', {
        'tag': '773',
        'ind1': '0',
        'ind2': '8'
    })
    field773_w = etree.SubElement(field773, 'subfield', {'code': 'w'})
    if '(AT-OBV)' in ac:
        field773_w.text = ac
    else:
        field773_w.text = '(AT-OBV)' + ac
    # 'Enthalten in' to subfield i
    field773_i = etree.SubElement(field773, 'subfield', {'code': 'i'})
    field773_i.text = 'Enthalten in'
    # 'Zeitungsausschnitte 1898-1915 / Sammlung Hachette to subfield t
    field773_t = etree.SubElement(field773, 'subfield', {'code': 't'})
    field773_t.text = 'Zeitungsausschnitte 1898-1915 / Sammlung Hachette'
    # 'Mappe XXX' to subfield g
    field773_g = etree.SubElement(field773, 'subfield', {'code': 'g'})
    field773_g.text = 'Mappe ' + str(mappe)
    return record


def create_common(record):
    # Datafield 040 subfields a, b, c, d, e
    field040 = etree.SubElement(record, 'datafield', {
        'tag': '040',
        'ind1': ' ',
        'ind2': ' '
    })
    field040_a = etree.SubElement(field040, 'subfield', {'code': 'a'})
    field040_a.text = 'ONB'
    field040_b = etree.SubElement(field040, 'subfield', {'code': 'b'})
    field040_b.text = 'ger'
    field040_c = etree.SubElement(field040, 'subfield', {'code': 'c'})
    field040_c.text = 'ONB-HANNA'
    field040_d = etree.SubElement(field040, 'subfield', {'code': 'd'})
    field040_d.text = 'AT-OeNB'
    field040_e = etree.SubElement(field040, 'subfield', {'code': 'e'})
    field040_e.text = 'rnab'
    # Datafield 090 subfield v
    field090 = etree.SubElement(record, 'datafield', {
        'tag': '090',
        'ind1': ' ',
        'ind2': ' '
    })
    field090_v = etree.SubElement(field090, 'subfield', {'code': 'v'})
    field090_v.text = '2'
    # Datafield 336 subfield b
    field336 = etree.SubElement(record, 'datafield', {
        'tag': '336',
        'ind1': ' ',
        'ind2': ' '
    })
    field336_b = etree.SubElement(field336, 'subfield', {'code': 'b'})
    field336_b.text = 'txt'
    # Datafield 337 subfield b
    field337 = etree.SubElement(record, 'datafield', {
        'tag': '337',
        'ind1': ' ',
        'ind2': ' '
    })
    field337_b = etree.SubElement(field337, 'subfield', {'code': 'b'})
    field337_b.text = 'n'
    # Datafield 338 subfield b
    field338 = etree.SubElement(record, 'datafield', {
        'tag': '338',
        'ind1': ' ',
        'ind2': ' '
    })
    field338_b = etree.SubElement(field338, 'subfield', {'code': 'b'})
    field338_b.text = 'nb'
    # Datafield 655 subfield a
    field655 = etree.SubElement(record, 'datafield', {
        'tag': '655',
        'ind1': ' ',
        'ind2': '4'
    })
    field655_b = etree.SubElement(field655, 'subfield', {'code': 'a'})
    field655_b.text = 'Zeitungsausschnitt'
    # Datafield 970 subfields a, d
    field970 = etree.SubElement(record, 'datafield', {
        'tag': '970',
        'ind1': '2',
        'ind2': ' '
    })
    field970_a = etree.SubElement(field970, 'subfield', {'code': 'a'})
    field970_a.text = 'ONB'
    field970_d = etree.SubElement(field970, 'subfield', {'code': 'd'})
    field970_d.text = 'NAK'
    # Datafield 972 subfields a
    field972 = etree.SubElement(record, 'datafield', {
        'tag': '972',
        'ind1': '0',
        'ind2': ' '
    })
    field972_a = etree.SubElement(field972, 'subfield', {'code': 'a'})
    field972_a.text = 'N-ZNAK'
    # Datafield 980 subfields, a, 9
    field980 = etree.SubElement(record, 'datafield', {
        'tag': '980',
        'ind1': '0',
        'ind2': ' '
    })
    field980_a = etree.SubElement(field980, 'subfield', {'code': 'a'})
    field980_a.text = 'zesp'
    field980_9 = etree.SubElement(field980, 'subfield', {'code': '9'})
    field980_9.text = 'LOCAL'
    # Datafield 980 subfields a, d 9
    field980 = etree.SubElement(record, 'datafield', {
        'tag': '980',
        'ind1': '0',
        'ind2': ' '
    })
    field980_a = etree.SubElement(field980, 'subfield', {'code': 'a'})
    field980_a.text = 'ONB-HANNA'
    field980_d = etree.SubElement(field980, 'subfield', {'code': 'd'})
    field980_d.text = 'AT-OeNB-ESP'
    field980_9 = etree.SubElement(field980, 'subfield', {'code': '9'})
    field980_9.text = 'LOCAL'
    return record


if __name__ == '__main__':
    # Load CSV files into pandas df
    csv_files_dir = '../artifacts/mapping_cleaned_links'
    csv_files = [f for f in os.listdir(csv_files_dir) if f.endswith('.csv')]
    csv_files = [os.path.join(csv_files_dir, elem) for elem in csv_files]
    csv_files.sort()
    csv_read_func = partial(pd.read_csv, delimiter=';')
    df = pd.concat(map(csv_read_func, csv_files), ignore_index=True)
    count = 0
    for chunk in np.array_split(df, 5):
        root = create_skeleton()
        for row in chunk.itertuples():
            root = create_record(root, row)
        xml_string = etree.tostring(root,
                                    pretty_print=True,
                                    xml_declaration=True,
                                    encoding='utf-8',
                                    standalone=True)
        with open(f'chunk-{count}.xml', 'wb') as f:
            f.write(xml_string)
        count += 1
