import logging
import os
import time

import numpy as np
# from numba import cuda, typed
import pandas as pd
import vaex
from joblib import Parallel, delayed
from pandas.errors import InvalidIndexError

from ..config import constantes
from ..config import settings
from ..database.mongo_collections import MONGO_COLLECTION_PROC_RECONCILED_CNF
from ..enums.enums import DataTypeEnum
from ..log.logger import get_logger, get_path_base
from ..utility.utils import get_start_end_day_month

# import cudf
# import torch

# Configura las opciones de Pandas para mejorar el rendimiento
pd.options.mode.chained_assignment = None  # Deshabilita las advertencias de asignaciones
chunk_size: int = 100000  # Tamaño de cada fragmento de lectura

# Item en donde se produce el error en el proceso de Consolidación
item_error = ''

path_log = os.path.join(get_path_base(), 'logging.conf')
log = get_logger(os.path.normpath(path_log))


# Funciones Privadas
def __getfile_from_path(sub_path: str, filename: str, ext: str):
    return os.path.join(settings.FLASK_UPLOAD_FOLDER, sub_path, f'{filename}{ext}')


# @cuda.jit(target="cuda")
def __get_columns_dataframe(file, ext: str):
    cols = None
    if ext == f'.{settings.FILE_EXT_TXT}':
        pass
    elif ext == f'.{settings.FILE_EXT_CSV}':
        cols = pd.read_csv(file, sep=settings.CSV_SEPARATOR).to_dict()
    elif ext == f'.{settings.FILE_EXT_XLS}' or ext == f'.{settings.FILE_EXT_XLSX}':
        cols = pd.read_excel(file.read(), nrows=1).to_dict()
    else:
        pass

    return __remove_spaces_name(cols)


# @cuda.jit(target="cuda")
def __get_dataframe(ext: str, file_name: str, path_file: str):
    df = pd.DataFrame()
    if ext == f'.{settings.FILE_EXT_TXT}':
        pass
    elif ext == f'.{settings.FILE_EXT_CSV}':
        df = pd.read_csv(path_file, sep=settings.CSV_SEPARATOR, dayfirst=True)
    elif ext == f'.{settings.FILE_EXT_XLS}' or ext == f'.{settings.FILE_EXT_XLSX}':
        dfs = __read_excel(path_file)
        df = pd.concat(dfs, ignore_index=True)
    else:
        df = vaex.open(path=f'{path_file}', group=f'{file_name}').to_pandas_df()

    return df


def __read_excel(path_file: str):
    xls = pd.ExcelFile(path_file)
    nombres_hojas = xls.sheet_names
    dfs = Parallel(n_jobs=-1)(delayed(__loop_read_excel)(path_file, hoja) for hoja in nombres_hojas)
    return dfs


# @cuda.jit(target="cuda")
def __loop_read_excel(file, sheet_name):
    return pd.read_excel(file, sheet_name=sheet_name)


# @cuda.jit(target="cuda")
def __normalized_data(df, key: str, type_data: str):
    if type_data == DataTypeEnum.DATE.value:
        df[f'{key}'] = pd.to_datetime(df[f'{key}'], format='mixed')
    elif type_data == DataTypeEnum.STRING.value:
        df[f'{key}'] = df[f'{key}'].fillna('-').astype(str).apply(lambda x: x.strip())
    elif type_data == DataTypeEnum.INTEGER.value:
        df[f'{key}'] = df[f'{key}'].astype(float).astype('Int64').fillna(0)
    elif type_data == DataTypeEnum.FLOAT.value:
        df[f'{key}'] = df[f'{key}'].astype(float).astype('Int64').fillna(0)
    else:
        df[f'{key}'] = df[f'{key}'].fillna('-').astype(str).apply(lambda x: x.strip())

    return df[f'{key}']


def __remove_spaces_name_columns(df):
    trim_name_columns = []
    for col in df.columns:
        trim_name_columns.append(col.strip())
    df.columns = trim_name_columns
    return df


def __remove_spaces_name(cols):
    trim_name_columns = []
    for col in cols:
        trim_name_columns.append(col.strip())
    return trim_name_columns


def __change_data_nat_nan_to_str(df):
    for col in df.columns:
        if df[col].dtype == np.dtype('object'):
            df[col] = df[col].where(df[col].notnull(), '')
            df[col] = df[col].where(df[col].notna(), '')
            df[col] = df[col].astype("string")
        elif df[col].dtype == np.dtype('datetime64[ns]'):
            df[col] = df[col].astype(object).where(df[col].notnull(), '')
            df[col] = df[col].astype(object).where(df[col].notna(), '')
            df[col] = df[col].astype("string")
        elif df[col].dtype == np.dtype('float64') or df[col].dtype == np.dtype('int64'):
            df[col] = df[col].where(df[col].notnull(), 0)
            df[col] = df[col].where(df[col].notna(), 0)
            df[col] = df[col].astype("int64")

    return df


def __create_index_in_df(df, index_key: str, cols_list: list):
    init = False
    for col in cols_list:
        if not init:
            df[f'{index_key}'] = df[f'{col}'].fillna(0).astype(str).apply(lambda x: x.strip())
            init = True
        else:
            df[f'{index_key}'] = df[f'{index_key}'] + df[f'{col}'].fillna(0).astype(str).apply(lambda x: x.strip())

    if init:
        df.set_index(f'{index_key}', inplace=True)
    return df


def __sort_datetime_data(df, column_name: str):
    return df.sort_values(by=[f'{column_name}'])


def __convert_datetime_to_str(df, key: str, type_data: str):
    if type_data == DataTypeEnum.DATE.value:
        df[f'{key}'] = df[f'{key}'].dt.strftime('%Y-%m-%d %H:%M:%S')

    return df[f'{key}']


def __get_duplicate(df):
    return df.loc[df.duplicated(keep=False)]


def __delete_duplicate(df, columns):
    return df.drop_duplicates(columns)


def __concat_data(df_origin, df_compared, union_columns=False):
    df_origin = df_origin.loc[~df_origin.index.duplicated(keep='first')]
    df_compared = df_compared.loc[~df_compared.index.duplicated(keep='first')]
    return pd.concat([df_origin, df_compared], axis=union_columns)


# @cuda.jit(target="cuda")
def __merge_data(df_origin, df_compared, name_index, is_common=True):
    _suffixes = ('_primary', '_external')
    if is_common:
        df = pd.merge(
            df_origin,
            df_compared,
            on=name_index,
            suffixes=_suffixes,
            how='inner'
        )
    else:
        df = pd.merge(
            df_origin,
            df_compared,
            on=name_index,
            suffixes=_suffixes,
            how='outer',
            indicator=True
        ).query("_merge != 'both'")

    return df


def __get_totals_dataframe_by_column(df, key: str):
    return df[f'{key}'].sum()


def __get_data_filter_dates(
        df,
        column,
        start_day,
        end_day,
        month: int,
        year: int):
    if len(f'{month}') < 2:
        m = f'0{month}'
    else:
        m = month

    init_date = pd.to_datetime(f'{year}-{m}-0{start_day}').date()
    finish_date = pd.to_datetime(f'{year}-{m}-{end_day}').date()

    return df[
        (pd.to_datetime(df[f'{column}']).dt.date >= init_date) &
        (pd.to_datetime(df[f'{column}']).dt.date <= finish_date)
        ]


def __add_column_description(df_c, df_nc, idx_columns):
    msg_diff = ""
    msg_part = "en la columna"
    is_more = False
    for col in idx_columns:
        if not np.where(df_c[f'{col}_primary'] == df_nc[f'{col}']):
            if len(msg_diff) == 0:
                msg_diff = f"{col}"
            else:
                msg_diff += f", {col}"
                is_more = True

    if is_more:
        msg_part = "en las columnas"
    return f"Existe una diferencia {msg_part} {msg_diff}." if len(msg_diff) > 0 else ""


##########################################
# Functions Public's
##########################################
def extract_headers_file(file, ext: str):
    columns_list = []
    try:
        df_columns = __get_columns_dataframe(file, ext)

        if df_columns:
            for column in df_columns:
                columns_list.append({
                    "col_name": f"{column}",
                    "col_used": False,
                    "col_type": "",
                    "col_norm": False
                })

            return columns_list
    except Exception as e:
        logging.exception(e)

    return columns_list


def create_file_from_dataframe(df, name_file: str):
    df.to_csv(f"{settings.FLASK_EXPORT_FOLDER}/{name_file}.csv", index=True, sep=';', encoding='utf-8-sig')


# @cuda.jit(target="cuda")
def execute_process_conciliation(data: list, doc_id: str):
    global item_error
    result_process = {}
    try:
        data_item = data[0]
        # Definición de variables base
        columns_df = __remove_spaces_name(data_item['columns_df'])
        sub_path = os.path.join(
            f'{data_item["internal"]["partner"]}',
            f'{data_item["internal"]["year"]}',
            f'{data_item["internal"]["month"]}'
        )

        # Carga de datos
        # Proceso Primary v/s External
        file_name = f"{data_item['internal']['filename']}{data_item['internal']['extension']}"
        item_error = f"Error lectura archivo: {file_name}"
        df_original = __get_dataframe(
            constantes.H5_FILE,  # data_item['internal']['extension'],
            data_item['internal']['filename'],
            __getfile_from_path(sub_path, data_item['internal']['filename'], constantes.H5_FILE)
        )

        file_name = f"{data_item['external'][0]['filename']}{data_item['external'][0]['extension']}"
        item_error = f"Error lectura archivo: {file_name}"
        df_compared = __get_dataframe(
            constantes.H5_FILE,  # data_item['external'][0]['extension'],
            data_item['external'][0]['filename'],
            __getfile_from_path(sub_path, data_item['external'][0]['filename'], constantes.H5_FILE)
        )

        # Obtiene fechas para proceso de filtrado de data para ejecución de consolidación de datos
        start_day, end_day = get_start_end_day_month(
            int(data_item["internal"]["month"]),
            int(data_item["internal"]["year"])
        )

        # Filtra datos por campo fecha
        date_column_filter = data_item['columns_date'][0]
        df_original = __get_data_filter_dates(
            df_original,
            date_column_filter,
            start_day,
            end_day,
            int(data_item["internal"]["month"]),
            int(data_item["internal"]["year"])
        )

        # df_compared = __get_data_filter_dates( df_compared, date_column_filter, start_day, end_day, int(data_item[
        # "internal"]["month"]), int(data_item["internal"]["year"]) )

        # TODO: Acá debiese filtrarse la información con
        #  respecto a la fecha definida dentro del ciclo generado -->
        #  df.loc[df['column_name'] == some_value]

        # Normaliza nombres de columnas / Eliminación de espacios en nombres
        item_error = f"Error al eliminar los espacios en las cabeceras fuente primaria"
        df_original = __remove_spaces_name_columns(df_original)
        item_error = f"Error al eliminar los espacios en las cabeceras fuente externa: {df_compared}"
        df_compared = __remove_spaces_name_columns(df_compared)

        # Normalización de los datos
        item_error = f"Error al normalizar los datos"
        for key, value in data_item['columns_normalized'].items():
            key = key.strip()
            df_original[f'{key}'] = __normalized_data(df_original, key, value)
            df_compared[f'{key}'] = __normalized_data(df_compared, key, value)

        # Definición de Índices
        item_error = f"Error al definir los indices"
        df_original = __create_index_in_df(df_original, 'idx', data_item['columns_index_df'])
        df_compared = __create_index_in_df(df_compared, 'idx', data_item['columns_index_df'])

        # Definición de campos a considerar dentro del DataFrame
        item_error = f"Error al definir los campos a usar en la consolidación"
        df_original = df_original[columns_df]
        df_compared = df_compared[columns_df]

        # Se ordenan los datos por fecha
        item_error = f"Error al ordenar la data por campo fecha"
        for key, value in data_item['columns_normalized'].items():
            key = key.strip()
            if value == 'datetime':
                df_original = __sort_datetime_data(df_original, key)
                df_compared = __sort_datetime_data(df_compared, key)

        # Se extraen los registros duplicados de cada origen
        item_error = f"Error al extraen los registros duplicados"
        df_duplicated_original = __get_duplicate(df_original)
        df_duplicated_compared = __get_duplicate(df_compared)

        # Elimina los registros duplicados
        item_error = f"Error al eliminar los registros duplicados"
        df_original = __delete_duplicate(df_original, data_item['columns_index_df'])
        df_compared = __delete_duplicate(df_compared, data_item['columns_index_df'])

        # Identificación de las transacciones conciliadas
        item_error = f"Error al identificar las transacciones conciliadas"
        df_reconciled = __merge_data(df_original, df_compared, 'idx')

        # Identificación de las transacciones no conciliadas
        item_error = f"Error al identificar las transacciones no conciliadas"
        df_unreconciled = __merge_data(df_reconciled, df_original, 'idx', False)

        # Identificación de las transacciones duplicadas
        item_error = f"Error al identificar las transacciones duplicadas"
        df_duplicated = __concat_data(df_duplicated_original, df_duplicated_compared, False)

        # Convierte campos datetime a str para conversion a JSON
        item_error = f"Error al tratar de convertir datos a texto"
        for key, value in data_item['columns_normalized'].items():
            key = key.strip()
            df_reconciled[f'{key}_primary'] = __convert_datetime_to_str(df_reconciled, f'{key}_primary', value)
            df_reconciled[f'{key}_external'] = __convert_datetime_to_str(df_reconciled, f'{key}_external', value)
            df_unreconciled[f'{key}_primary'] = __convert_datetime_to_str(df_unreconciled, f'{key}_primary', value)
            df_unreconciled[f'{key}_external'] = __convert_datetime_to_str(df_unreconciled, f'{key}_external', value)

            if key in df_reconciled:
                df_reconciled[f'{key}'] = __convert_datetime_to_str(df_reconciled, f'{key}', value)

            if key in df_unreconciled:
                df_unreconciled[f'{key}'] = __convert_datetime_to_str(df_unreconciled, f'{key}', value)

            if key in df_duplicated:
                df_duplicated[f'{key}'] = __convert_datetime_to_str(df_duplicated, f'{key}', value)

        # Normalización de campos vacíos (nulos)
        item_error = f"Error al tratar de normalizar campos vacíos (nulos)"
        for key, value in data_item['columns_normalized'].items():
            key = key.strip()
            df_reconciled[f'{key}_primary'] = __normalized_data(df_reconciled, f'{key}_primary', value)
            df_reconciled[f'{key}_external'] = __normalized_data(df_reconciled, f'{key}_external', value)
            df_unreconciled[f'{key}_primary'] = __normalized_data(df_unreconciled, f'{key}_primary', value)
            df_unreconciled[f'{key}_external'] = __normalized_data(df_unreconciled, f'{key}_external', value)
            df_duplicated[f'{key}'] = __normalized_data(df_duplicated, f'{key}', value)

        df_reconciled = __change_data_nat_nan_to_str(df_reconciled)
        df_unreconciled = __change_data_nat_nan_to_str(df_unreconciled)
        df_duplicated = __change_data_nat_nan_to_str(df_duplicated)

        # Agrega columna con descripción para identificación de problema en no conciliados.
        # __add_column_description(df_reconciled, df_unreconciled, data_item['columns_index_df'])

        # Generación de totales
        total_reconciled = 0
        total_unreconciled = 0
        total_duplicated = 0
        if len(data_item['columns_amount']) > 0:
            amount_column = data_item['columns_amount'][0]

            df_reconciled[f"{amount_column}_external"] = pd.to_numeric(df_reconciled[f"{amount_column}_external"])
            df_unreconciled[f"{amount_column}"] = pd.to_numeric(df_unreconciled[f"{amount_column}"])
            df_duplicated[f"{amount_column}"] = pd.to_numeric(df_duplicated[f"{amount_column}"])

            total_reconciled = df_reconciled[f"{amount_column}_external"].sum()
            total_unreconciled = df_unreconciled[f"{amount_column}"].sum()
            total_duplicated = df_duplicated[f"{amount_column}"].sum()

        # Se genera diccionario y se agrega campo de referencia para búsqueda
        item_error = f"Error al tratar de generar salida datos (Conciliados)"
        df_reconciled = df_reconciled.to_dict('records')
        idx = 1
        for df_rec in df_reconciled:
            df_rec[f'{MONGO_COLLECTION_PROC_RECONCILED_CNF}_id'] = doc_id
            df_rec['item'] = idx
            df_rec['type_process'] = 'primary'
            idx += 1

        item_error = f"Error al tratar de generar salida datos (No Conciliados)"
        df_unreconciled = df_unreconciled.to_dict('records')
        idx = 1
        for df_unrec in df_unreconciled:
            df_unrec[f'{MONGO_COLLECTION_PROC_RECONCILED_CNF}_id'] = doc_id
            df_unrec['item'] = idx
            df_unrec['type_process'] = 'primary'
            idx += 1

        df_duplicated = df_duplicated.to_dict('records')
        item_error = f"Error al tratar de generar salida datos (Duplicados)"
        idx = 1
        for df_dup in df_duplicated:
            df_dup[f'{MONGO_COLLECTION_PROC_RECONCILED_CNF}_id'] = doc_id
            df_dup['item'] = idx
            df_dup['type_process'] = 'primary'
            idx += 1

        df_duplicated_compared = df_duplicated_compared.to_dict('records')
        item_error = f"Error al tratar de generar salida datos (Duplicados.)"
        idx = 1
        for df_dup_com in df_duplicated_compared:
            df_dup_com[f'{MONGO_COLLECTION_PROC_RECONCILED_CNF}_id'] = doc_id
            df_dup_com['item'] = idx
            df_dup_com['type_process'] = 'primary'
            idx += 1

        result_process["primary-process"] = {
            "total_reconciled": total_reconciled,
            "data_reconciled": df_reconciled,
            "total_unreconciled": total_unreconciled,
            "data_unreconciled": df_unreconciled,
            "data_duplicated": df_duplicated,
            "total_duplicated": total_duplicated
        }

        # Proceso External v/s Primary
        file_name = f"{data_item['external'][0]['filename']}{data_item['external'][0]['extension']}"
        item_error = f"Error lectura archivo: {file_name}"
        df_original = __get_dataframe(
            constantes.H5_FILE,  # data_item['external'][0]['extension'],
            data_item['external'][0]['filename'],
            __getfile_from_path(sub_path, data_item['external'][0]['filename'], constantes.H5_FILE)
        )

        file_name = f"{data_item['internal']['filename']}{data_item['internal']['extension']}"
        item_error = f"Error lectura archivo: {file_name}"
        df_compared = __get_dataframe(
            constantes.H5_FILE,  # data_item['internal']['extension'],
            data_item['internal']['filename'],
            __getfile_from_path(sub_path, data_item['internal']['filename'], constantes.H5_FILE)
        )

        # Normaliza nombres de columnas / Eliminación de espacios en nombres
        item_error = f"Error al eliminar los espacios en las cabeceras fuente primaria"
        df_original = __remove_spaces_name_columns(df_original)
        item_error = f"Error al eliminar los espacios en las cabeceras fuente externa"
        df_compared = __remove_spaces_name_columns(df_compared)

        # Normalización de los datos
        item_error = f"Error al normalizar los datos"
        for key, value in data_item['columns_normalized'].items():
            key = key.strip()
            df_original[f'{key}'] = __normalized_data(df_original, key, value)
            df_compared[f'{key}'] = __normalized_data(df_compared, key, value)

        # Definición de Índices
        item_error = f"Error al definir los indices"
        df_original = __create_index_in_df(df_original, 'idx', data_item['columns_index_df'])
        df_compared = __create_index_in_df(df_compared, 'idx', data_item['columns_index_df'])

        # Definición de campos a considerar dentro del DataFrame
        item_error = f"Error al definir los campos a usar en la consolidación"
        df_original = df_original[columns_df]
        df_compared = df_compared[columns_df]

        # Se ordenan los datos por fecha
        item_error = f"Error al ordenar la data por campo fecha"
        for key, value in data_item['columns_normalized'].items():
            key = key.strip()
            if value == 'datetime':
                df_original = __sort_datetime_data(df_original, key)
                df_compared = __sort_datetime_data(df_compared, key)

        # Se extraen los registros duplicados de cada origen
        item_error = f"Error al extraen los registros duplicados"
        df_duplicated_original = __get_duplicate(df_original)
        df_duplicated_compared = __get_duplicate(df_compared)

        # Elimina los registros duplicados
        item_error = f"Error al eliminar los registros duplicados"
        df_original = __delete_duplicate(df_original, data_item['columns_index_df'])
        df_compared = __delete_duplicate(df_compared, data_item['columns_index_df'])

        # Identificación de las transacciones conciliadas
        item_error = f"Error al identificar las transacciones conciliadas"
        df_reconciled = __merge_data(df_original, df_compared, 'idx')

        # Identificación de las transacciones no conciliadas
        item_error = f"Error al identificar las transacciones no conciliadas"
        df_unreconciled = __merge_data(df_reconciled, df_original, 'idx', False)

        # Identificación de las transacciones duplicadas
        item_error = f"Error al identificar las transacciones duplicadas"
        df_duplicated = __concat_data(df_duplicated_original, df_duplicated_compared, False)

        # Convierte campos datetime a str para conversion a JSON
        item_error = f"Error al tratar de convertir datos a texto"
        for key, value in data_item['columns_normalized'].items():
            key = key.strip()
            df_reconciled[f'{key}_primary'] = __convert_datetime_to_str(df_reconciled, f'{key}_primary', value)
            df_reconciled[f'{key}_external'] = __convert_datetime_to_str(df_reconciled, f'{key}_external', value)
            df_unreconciled[f'{key}_primary'] = __convert_datetime_to_str(df_unreconciled, f'{key}_primary', value)
            df_unreconciled[f'{key}_external'] = __convert_datetime_to_str(df_unreconciled, f'{key}_external', value)

            if key in df_reconciled:
                df_reconciled[f'{key}'] = __convert_datetime_to_str(df_reconciled, f'{key}', value)

            if key in df_unreconciled:
                df_unreconciled[f'{key}'] = __convert_datetime_to_str(df_unreconciled, f'{key}', value)

            if key in df_duplicated:
                df_duplicated[f'{key}'] = __convert_datetime_to_str(df_duplicated, f'{key}', value)

        # Normalización de campos vacíos (nulos)
        item_error = f"Error al tratar de normalizar campos vacíos (nulos)"
        for key, value in data_item['columns_normalized'].items():
            key = key.strip()
            df_reconciled[f'{key}_primary'] = __normalized_data(df_reconciled, f'{key}_primary', value)
            df_reconciled[f'{key}_external'] = __normalized_data(df_reconciled, f'{key}_external', value)
            df_unreconciled[f'{key}_primary'] = __normalized_data(df_unreconciled, f'{key}_primary', value)
            df_unreconciled[f'{key}_external'] = __normalized_data(df_unreconciled, f'{key}_external', value)
            df_duplicated[f'{key}'] = __normalized_data(df_duplicated, f'{key}', value)

        df_reconciled = __change_data_nat_nan_to_str(df_reconciled)
        df_unreconciled = __change_data_nat_nan_to_str(df_unreconciled)
        df_duplicated = __change_data_nat_nan_to_str(df_duplicated)

        # Generación de totales
        total_reconciled = 0
        total_unreconciled = 0
        total_duplicated = 0
        if len(data_item['columns_amount']) > 0:
            amount_column = data_item['columns_amount'][0]

            df_reconciled[f"{amount_column}_external"] = pd.to_numeric(df_reconciled[f"{amount_column}_external"])
            df_unreconciled[f"{amount_column}"] = pd.to_numeric(df_unreconciled[f"{amount_column}"])
            df_duplicated[f"{amount_column}"] = pd.to_numeric(df_duplicated[f"{amount_column}"])

            total_reconciled = df_reconciled[f"{amount_column}_external"].sum()
            total_unreconciled = df_unreconciled[f"{amount_column}"].sum()
            total_duplicated = df_duplicated[f"{amount_column}"].sum()

        # Se genera diccionario y se agrega campo de referencia para búsqueda
        item_error = f"Error al tratar de generar salida datos (Conciliados)"
        df_reconciled = df_reconciled.to_dict('records')
        idx = 1
        for df_rec in df_reconciled:
            df_rec[f'{MONGO_COLLECTION_PROC_RECONCILED_CNF}_id'] = doc_id
            df_rec['item'] = idx
            df_rec['type_process'] = 'secondary'
            idx += 1

        item_error = f"Error al tratar de generar salida datos (No Conciliados)"
        df_unreconciled = df_unreconciled.to_dict('records')
        idx = 1
        for df_unrec in df_unreconciled:
            df_unrec[f'{MONGO_COLLECTION_PROC_RECONCILED_CNF}_id'] = doc_id
            df_unrec['item'] = idx
            df_unrec['type_process'] = 'secondary'
            idx += 1

        df_duplicated = df_duplicated.to_dict('records')
        item_error = f"Error al tratar de generar salida datos (Duplicados)"
        idx = 1
        for df_dup in df_duplicated:
            df_dup[f'{MONGO_COLLECTION_PROC_RECONCILED_CNF}_id'] = doc_id
            df_dup['item'] = idx
            df_dup['type_process'] = 'secondary'
            idx += 1

        df_duplicated_compared = df_duplicated_compared.to_dict('records')
        item_error = f"Error al tratar de generar salida datos (Duplicados.)"
        idx = 1
        for df_dup_com in df_duplicated_compared:
            df_dup_com[f'{MONGO_COLLECTION_PROC_RECONCILED_CNF}_id'] = doc_id
            df_dup_com['item'] = idx
            df_dup_com['type_process'] = 'secondary'
            idx += 1

        result_process["secondary-process"] = {
            "total_reconciled": total_reconciled,
            "data_reconciled": df_reconciled,
            "total_unreconciled": total_unreconciled,
            "data_unreconciled": df_unreconciled,
            "data_duplicated": df_duplicated,
            "total_duplicated": total_duplicated
        }
        return result_process
    except TypeError as te:
        log.error(te)
        return {
            "msg": f"{item_error}.\nError en el tipo de datos: {te}"
        }
    except KeyError as te:
        log.error(te)
        return {
            "msg": f"{item_error}.\nClave no existe: {te}."
        }
    except AttributeError as te:
        log.error(te)
        return {
            "msg": f"{item_error}.\nError en el atributo: {te}"
        }
    except InvalidIndexError as te:
        log.error(te)
        return {
            "msg": f"{item_error}.\nError en la definición del indice: {te}"
        }
    except ValueError as te:
        log.error(te)
        return {
            "msg": f"{item_error}.\nValor erróneo: {te}"
        }
    except Exception as e:
        log.exception(e)
        return {
            "msg": f"{item_error}.\nError: {e}"
        }


def convert_file_to_hfs5(path_file, file_name, extension_file):
    start = time.time()
    path_file_hfs5 = __getfile_from_path(path_file, file_name, constantes.H5_FILE)
    path_file_origin = __getfile_from_path(path_file, file_name, extension_file)

    # Carga de datos
    if extension_file == f'.{settings.FILE_EXT_CSV}':
        df = __get_dataframe(extension_file, file_name, path_file_origin)
    else:
        dfs = __read_excel(path_file_origin)
        df = pd.concat(dfs, ignore_index=True)

    df = df.astype(str)
    df = __remove_spaces_name_columns(df)

    # Convertir el DataFrame de Pandas a un DataFrame de Vaex
    df_vaex = vaex.from_pandas(df, copy_index=False)

    # Convertir todos los datos en cadenas de texto
    for columna in df_vaex.get_column_names():
        df_vaex[columna] = df_vaex[columna].astype('str')

    # Guardar el DataFrame de Vaex en formato HDF5
    df_vaex.export_hdf5(path_file_hfs5, mode='w', group=f'{file_name}')

    end = time.time()
    log.info(f"Tiempo de creación archivo HDF5: {round((end - start) / 60, 2)}")
    try:
        os.remove(path_file_origin)
    except OSError:
        pass
