import logging
import os
from enum import Enum
from typing import List, cast, Type

from inscriptis import Inscriptis
from inscriptis.css_profiles import RELAXED_CSS_PROFILE
from inscriptis.html_properties import Display
from inscriptis.model.config import ParserConfig
from inscriptis.model.css import HtmlElement
from lxml import etree as ET
from pydantic import Field, BaseModel
from pymultirole_plugins.v1.converter import ConverterParameters, ConverterBase
from pymultirole_plugins.v1.schema import Document, Sentence, Boundary
from starlette.datastructures import UploadFile

_home = os.path.expanduser("~")


class InputFormat(str, Enum):
    XML_Articles = "XML Articles"
    Url_List = "Url List"


class CairnInfoXmlParameters(ConverterParameters):
    input_format: InputFormat = Field(
        InputFormat.XML_Articles,
        description="""Format d'entrée du fichier, parmi:<br/>
        <li>`XML Articles`: fichier XML au format Cainr.info.<br/>
        <li>`Url List`: fichier texte contenant une url par ligne.""", extra="internal"
    )
    bilbio: bool = Field(True, description="Extraire la bibliographie")
    notes: bool = Field(True, description="Extraire les notes")
    resume: bool = Field(True, description="Extraire le résumé")


logger = logging.getLogger("pymultirole")


class CairnInfoXmlConverter(ConverterBase):
    """CairnInfoXml converter ."""

    def convert(
            self, source: UploadFile, parameters: ConverterParameters
    ) -> List[Document]:
        params: CairnInfoXmlParameters = cast(CairnInfoXmlParameters, parameters)

        docs = []
        if params.input_format == InputFormat.Url_List:
            pass
        elif params.input_format == InputFormat.XML_Articles:
            tree = ET.parse(source.file)
            for article in tree.iter("article"):
                sentences = []
                boundaries = []
                text = ""
                identifier = article.get('idproprio')
                typeart = article.get('typeart')
                # lang = article.get('lang')
                liminaire = article.find("liminaire")
                title = None
                metadata = {'typeart': typeart}
                if liminaire is not None:
                    grtitre = liminaire.find("grtitre")
                    if grtitre is not None:
                        titre = grtitre.find("titre")
                        if titre is not None:
                            title, text, sentences = add_paragraph(tree, text, sentences, titre)
                        surtitre = grtitre.find("surtitre")
                        if surtitre is not None:
                            metadata["surtitre"] = surtitre.text
                        sstitre = grtitre.find("sstitre")
                        if sstitre is not None:
                            metadata["sstitre"] = sstitre.text
                    grauteur = liminaire.find("grauteur")
                    if grauteur is not None:
                        auteurs = []
                        for auteur in grauteur.iter("auteur"):
                            prenom_nom = []
                            nompers = auteur.find("nompers")
                            if nompers is not None:
                                prenom = nompers.find("prenom")
                                if prenom is not None:
                                    prenom_nom.append(prenom.text)
                                nomfamille = nompers.find("nomfamille")
                                if nomfamille is not None:
                                    nomfamille = nompers.find("nomfamille")
                                    prenom_nom.append(nomfamille.text)
                            affiliation = auteur.find("affiliation")
                            affiliations = []
                            if affiliation is not None:
                                for alinea in affiliation.iter("alinea"):
                                    alinea_txt = alinea.text.strip()
                                    if alinea_txt:
                                        affiliations.append(alinea.text)
                            auteur_affiliation = f"{' '.join(prenom_nom)}"
                            if affiliations:
                                auteur_affiliation += f" ({', '.join(affiliations)})"
                            auteurs.append(auteur_affiliation)
                        metadata["auteur"] = auteurs
                    chapo = liminaire.find("chapo")
                    if chapo is not None:
                        _, text, sentences = add_paragraph(tree, text, sentences, chapo)
                    if params.resume:
                        resume = liminaire.find("resume")
                        if resume is not None and resume.get('lang', 'fr') == 'fr':
                            _, text, sentences = add_paragraph(tree, text, sentences, resume)

                admin = article.find("admin")
                if admin is not None:
                    infoarticle = admin.find("infoarticle")
                    if infoarticle is not None:
                        section_sommaire = infoarticle.find("section_sommaire")
                        if section_sommaire is not None:
                            metadata["section_sommaire"] = section_sommaire.text
                        sous_section_sommaire = infoarticle.find("sous_section_sommaire")
                        if sous_section_sommaire is not None:
                            metadata["sous_section_sommaire"] = sous_section_sommaire.text
                        nbpage = infoarticle.find("nbpage")
                        if nbpage is not None:
                            metadata["nbpage"] = nbpage.text
                        typepub = infoarticle.find("typepub")
                        if typepub is not None:
                            metadata["typepub"] = typepub.text
                        grdiscipline = infoarticle.find("grdiscipline")
                        if grdiscipline is not None:
                            disciplines = []
                            for discipline in grdiscipline.iter("discipline"):
                                disciplines.append(discipline.text)
                            metadata["discipline"] = disciplines
                        grthematique = infoarticle.find("grthematique")
                        if grthematique is not None:
                            thematiques = []
                            for thematique in grthematique.iter("thematique"):
                                thematiques.append(thematique.text)
                            metadata["thematique"] = thematiques
                    numero = admin.find("numero")
                    if numero is not None:
                        theme = numero.find("theme")
                        if theme is not None:
                            metadata["titre_numero"] = theme.text
                        pub = numero.find("pub")
                        if pub is not None:
                            metadata["année"] = pub.findtext('annee')
                    revue = admin.find("revue")
                    if revue is not None:
                        titrerev = revue.find("titrerev")
                        if titrerev is not None:
                            metadata["titre_revue"] = titrerev.text
                    collection = admin.find("collection")
                    if collection is None:
                        ouvrage = admin.find("ouvrage")
                        if ouvrage is not None:
                            collection = ouvrage.find("collection")
                    if collection is not None:
                        titrecol = collection.find("titre")
                        if titrecol is not None:
                            metadata["titre_collection"] = titrecol.text

                corps = article.find("corps")
                if corps is not None:
                    for section in corps:
                        if section.tag in ['section1', 'section2', 'section3', 'section4', 'section5', 'section6']:
                            _, text, sentences, boundaries = add_section(tree, text, sentences, boundaries, section)
                partiesann = article.find("partiesann")
                if partiesann is not None:
                    if params.bilbio:
                        biblio = partiesann.find("biblio")
                        if biblio is not None:
                            _, text, sentences, boundaries = add_section(tree, text, sentences, boundaries, biblio)
                    if params.notes:
                        grnote = partiesann.find("grnote")
                        if grnote is not None:
                            _, text, sentences, boundaries = add_section(tree, text, sentences, boundaries, grnote)
                doc = Document(identifier=identifier, title=title, text=text, metadata=metadata, sentences=sentences,
                               boundaries={'SECTIONS': boundaries})
                doc.metadata['original'] = source.filename
                docs.append(doc)
        return docs

    @classmethod
    def get_model(cls) -> Type[BaseModel]:
        return CairnInfoXmlParameters


MY_CSS_PROFILE = RELAXED_CSS_PROFILE.copy()
MY_CSS_PROFILE["titre"] = HtmlElement('h1', display=Display.block, margin_before=1,
                                      margin_after=1)
MY_CSS_PROFILE["biblio"] = HtmlElement('h2', display=Display.block, margin_before=1,
                                       margin_after=1)
MY_CSS_PROFILE["divbiblio"] = HtmlElement('h3', display=Display.block, margin_before=1,
                                          margin_after=1)
MY_CSS_PROFILE["alinea"] = HtmlElement('p', display=Display.block,
                                       padding=2)
MY_CSS_PROFILE["refbiblio"] = HtmlElement('div', display=Display.block,
                                          padding=2)
MY_CSS_PROFILE["listenonord"] = HtmlElement('ul', display=Display.block, margin_before=0,
                                            margin_after=0, padding=4)
MY_CSS_PROFILE["listeord"] = HtmlElement('ol', display=Display.block, margin_before=0,
                                         margin_after=0, padding=4)
MY_CSS_PROFILE["elemliste"] = HtmlElement('li', display=Display.block)


def add_section(tree, text, sentences, boundaries, section):
    title = None
    bstart = len(text)
    has_title = section.find("titre")
    if has_title is not None:
        title = has_title.text
        if title is not None:
            sstart = len(text)
            text += title + '\n'
            sentences.append(Sentence(start=sstart, end=len(text), metadata={'xpath': tree.getpath(has_title)}))
        else:
            pass
    for child in section.iterdescendants():
        if child.tag in ['para', 'encadre', 'listenonord', 'listeord', 'tableau', 'refbiblio', 'note']:
            _, text, sentences = add_paragraph(tree, text, sentences, child, header=title)
    boundaries.append(Boundary(start=bstart, end=len(text), name=tree.getpath(section)))
    return title, text, sentences, boundaries


def add_paragraph(tree, text, sentences, item, header=None):
    inscriptis = Inscriptis(
        item, ParserConfig(css=MY_CSS_PROFILE)
    )
    item_text = inscriptis.get_text()
    sstart = len(text)
    text += item_text + '\n'
    metadata = {'xpath': tree.getpath(item)}
    if header is not None:
        metadata['header'] = header
    sentences.append(Sentence(start=sstart, end=len(text), metadata=metadata))
    return item_text, text, sentences
