#%%
import json

from .utilities.parsing_utilities import interpreter, inizialeraddoppiata, list_normalize

# Basic data provider class; can be instantiated to handle different kinds
# of data-providing connections or interfaces based on config options
from .data_interface.data_providers_setup import queryHandlerFactory

import pandas as pd
import math

# Main class for basic queries contains:
# - a data provider instance
# - methods to submit queries to the data provider instance
class basicQueries:

    def __init__(self, dataConfig):
        self.queryHandler = queryHandlerFactory(dataConfig)
        self.listOcc = dataConfig.get('listOcc')

    # Prepares and sends query OBJECTS which will be processed by the data provider
    def sendBasicQuery(self, text, queryType, espansa, raddoppiata, pandas=False, dbFile=None):
        
        entries = interpreter(text)
        
        data = entries
        dataNorm = []
        if raddoppiata==1:
            data = entries + inizialeraddoppiata(entries)
        if espansa==1 and raddoppiata==0:
            dataNorm = list_normalize(entries)
        elif espansa==1 and raddoppiata==1:
            dataNorm = entries + list_normalize(inizialeraddoppiata(entries))

        return self.queryHandler.query({'data': data, 'dataNorm': dataNorm, 'queryType': queryType}, pandas, dbFile)
    
    #%% ha in input le funzioni di ricerca, cerca nell'occorrenziario i puntatori ai contesti e altri elementi ad essi associati. 
    #l'attributo type definisce il tipo di ricerca in input (0 per forme, 1 per lemmi, 2 per lemmi con opzione "mostra occorrenze non lemmatizzate")
    def findtexts(self, type, df, index=None):
        # Check if the input is a DataFrame, and convert it if it is not
        if not isinstance(df, pd.DataFrame):
            df = pd.DataFrame(df)
        # If an index is provided, select the rows that correspond to the index
        if index is not None:
            if isinstance(index, range):
                index = list(index)
            elif not isinstance(index, list):
                index = [index]
            df = df.loc[index]
        # Create a list of 'cod' values from the input DataFrame
        codList = [row['cod'] for _, row in df.iterrows()]
        # Get the 'listOcc' values
        listOcc = self.listOcc
        # Create a dictionary with query information
        queryData = {'queryType': 'texts', 'querySubtype': type, 'codList': codList}
        # If 'type' is 2 (option "non lemmatizzate"), make an additional query to get form codes
        # NOTE: A this stage of development 'type 2' is default for "lemma" searches (this could be changed).
        if type == 2:
            subQueryData = {'queryType': 'pfl', 'codList': codList}
            subdf = self.queryHandler.query(subQueryData, pandas=True)
            queryData['formCodList'] = list(subdf['codForma'])
        # Make a query to each table in 'listOcc' and concatenate the results
        queryResponses = [self.queryHandler.query(dict(queryData, table=table), pandas=True) for table in listOcc]
        textlist = pd.concat(queryResponses)
        return textlist

    # %% ha in input findtexts, restituisce i contesti associati agli elementi localizzati.
    # Il range dei contesti è impostato di default a 30 parole e può essere rimodulato nel passaggio al contesto singolo.
    def findcontexts(self, textlist):
        # Initialize lists to store context information
        contexts = []
        formats = []
        minChar_list = []
        maxChar_list = []
        # Iterate over each row in 'textlist'
        for _, row in textlist.iterrows():
            sigla = row["sigla"]
            if row["piniz"] is None or math.isnan(row["piniz"]):
                minChar = int(row["backup_piniz"])
            else:
                minChar = int(row["piniz"])
            if row["pfin"] is None or math.isnan(row["pfin"]):
                maxChar = int(row["backup_pfin"])
            else:
                maxChar = int(row["pfin"])
            fileQueryData = {'sigla': sigla, 'minChar': minChar, 'maxChar': maxChar}
            # Call 'textQuery' to get the context and format information
            cont, form = self.queryHandler.textQuery(fileQueryData, True)
            # Append the results to the respective lists
            minChar_list.append(minChar)
            maxChar_list.append(maxChar)
            contexts.append(cont)
            formats.append(json.dumps(form))
        # Add the context information to 'textlist' and reset the index
        textlist['piniz'] = minChar_list
        textlist['pifin'] = maxChar_list
        textlist['contesto'] = contexts
        textlist['formattazione contesto'] = formats
        return textlist.reset_index(drop=True)

    # %% Ha in input findcontexts, associa i riferimenti bibliografici ad ogni contesto dal db BiblioTLIO.
    def findbib(self, contexts):
        # Old, deprecated
        # infobib = pd.DataFrame()
        # rif_org = pd.DataFrame()
        # for ind, row in contexts.iterrows():
        #     queryData = {'queryType': 'bib', 'row': row}
        #     bib = self.queryHandler.query(queryData, pandas=True, dbFile='bibliografia/BiblioTLIO.db')
        #     infobib = pd.concat([infobib, bib])
        #     queryData = {'queryType': 'rif', 'row': row}
        #     rif = self.queryHandler.query(queryData, pandas=True)
        #     rif_org = pd.concat([rif_org, rif])
        # annoiniz = list(infobib['Anno iniziale'])
        # annofin = list(infobib['Anno finale'])
        # datacod = list(infobib['Data codificata'])
        # datadesc = list(infobib['Data descrittiva'])
        # titoloabb = list(infobib['Titolo Abbreviato'])
        # autore = list(infobib['Autore'])
        # titolo = list(infobib['Titolo'])
        # curatore = list(infobib['Curatore'])
        # areagen = list(infobib['Area generica'])
        # areaspec = list(infobib['Area specifica'])
        # genere = list(infobib['Genere'])
        # forma = list(infobib['Forma'])
        # tipo = list(infobib['Tipo'])
        # iq = list(infobib['IQ'])

        siglaList = list(contexts['sigla'])
        siglaSet = set(siglaList)
        queryData = {'queryType': 'bibAlt', 'siglaSet': siglaSet}
        infobib = self.queryHandler.query(queryData, pandas=True, dbFile='bibliografia/BiblioTLIO.db')
        infobib = infobib.set_index('Sigla')
        infobib2 = {sigla: infobib.loc[sigla].to_dict() for sigla in siglaSet}
        infobib = [infobib2[sigla] for sigla in siglaList]
        #
        annoiniz = [el['Anno iniziale'] for el in infobib]
        annofin = [el['Anno finale'] for el in infobib]
        datacod = [el['Data codificata'] for el in infobib]
        datadesc = [el['Data descrittiva'] for el in infobib]
        titoloabb = [el['Titolo Abbreviato'] for el in infobib]
        autore = [el['Autore'] for el in infobib]
        titolo = [el['Titolo'] for el in infobib]
        curatore = [el['Curatore'] for el in infobib]
        areagen = [el['Area generica'] for el in infobib]
        areaspec = [el['Area specifica'] for el in infobib]
        genere = [el['Genere'] for el in infobib]
        forma = [el['Forma'] for el in infobib]
        tipo = [el['Tipo'] for el in infobib]
        iq = [el['IQ'] for el in infobib]

        ntxList = list(contexts['ntx'])
        numOrgList = list(contexts['numorg'])
        coordsList = [(numorg, ntxList[ind]) for ind, numorg in enumerate(numOrgList)]
        coordsSet = set(coordsList)
        queryData = {'queryType': 'rifAlt', 'coordsSet': coordsSet}
        rif_org = self.queryHandler.query(queryData, pandas=True)
        rif_org = rif_org.set_index( ['numorg', 'ntx'] )
        rif_org2 = {coord: rif_org.loc[coord] for coord in coordsSet}
        rif_org = [rif_org2[coord] for coord in coordsList]
        rif1 = [rif['Rif_organico'] for rif in rif_org]
        rif2 = [rif['Rif_completo'] for rif in rif_org]
        
        contexts['Anno iniziale'] = annoiniz
        contexts['Anno finale'] = annofin
        contexts['Data codificata'] = datacod
        contexts['Data descrittiva'] = datadesc
        contexts['Autore'] = autore
        contexts['Titolo Abbreviato'] = titoloabb
        contexts['Titolo'] = titolo
        contexts['Curatore'] = curatore
        contexts['Area generica'] = areagen
        contexts['Area specifica'] = areaspec
        contexts['Genere'] = genere
        contexts['Forma'] = forma
        contexts['Tipo'] = tipo
        contexts ['IQ'] = iq
        contexts['Rif_organico'] = rif1
        contexts['Rig_completo'] = rif2
        chrono = contexts.sort_values(by=['Data codificata', 'Rif_organico'])   
        return (chrono.reset_index(drop=True))