DEFAULT_MONGO_URI = 'mongodb://localhost:27017/'

time_series_options = {
    "timeseries": {
        "timeField": "timestamp",
        # "metaField": "metadata",
        "metaField": "camera_id",
        "granularity": "hours"
    }
}

def data_analytic_logger(text):
    print(text)


def ensure_data_analytic_collection( database, database_name='data_analytic' ):
    """ Args:
        - database: Instancia de la bd de MongoDB.
        - database_name: Nombre de la coleccion objetivo
        """

    if not exist_collection( database, database_name ):
        # Create handle for new time-series collection
        database.create_collection(database_name, **time_series_options)
        
        index_spec = [("camera_id", 1), ("timestamp", 1)]
        database[database_name].create_index(index_spec) 
        print() ; data_analytic_logger(f"La Colección {database_name} data no existía y se ha creado como serie de tiempo.")
        return
    else:
        ensure_index( database[database_name] )
    
     

def exist_collection( db, nombre_coleccion ):
    return nombre_coleccion in db.list_collection_names()

# 
def fetch_analytic_data(collection, center_id, start_day, end_day):
    """
    Obtiene los documentos de analítica almacenados en la base de datos para un rango de días y
    los ordena por día y por jaula, devolviendo un diccionario con el formato:
    
        {
          'dd/mm/YYYY': {
              <jaula_id>: { ...data... },
              <jaula_id2>: { ...data... },
              ...
          },
          ...
        }
    
    @params:
      - collection: Colección de MongoDB donde se almacenan los datos.
      - center_id (str): ID de centro, en formato string, correspondiente al identificador único de cada centro.
      - start_day (datetime): Fecha de inicio (incluida).
      - end_day (datetime): Fecha final (exclusiva).
    
    @returns:
      - grouped_data (dict): Datos agrupados por día y por jaula.

      {
        'dd/mm/YYYY': {
            <jaula_id>: { ...data... },
            ...
        },
        ...
      }
    """
    try:
        query = {
            "timestamp": {"$gte": start_day, "$lt": end_day},
            "centro_id": center_id
        }
        projection = {"_id": 0}

        #  Orden DESC para evitar bounded sorter en servidor
        cursor = collection.find(query, projection).sort("timestamp", -1)

        docs = list(cursor)

        # Revertimos en memoria para entregar ASC (misma funcionalidad original)
        docs.reverse()

    except Exception as e:
        data_analytic_logger(f"Error leyendo documentos de analítica {e.__traceback__.tb_lineno} : {e}")
        return {}

    # Agrupar por día/jaula
    grouped_data = {}
    for doc in docs:
        day = doc["timestamp"].strftime("%d/%m/%Y")
        grouped_data.setdefault(day, {})
        jaula_id = doc.get("camera_id")
        grouped_data[day][jaula_id] = doc.get("data", {})

    return grouped_data



# # def fetch_analytic_data( collection, center_id, start_day, end_day ):
#     """ Obtiene los documentos de analítica almacenados en la base de datos para un rango de días y
#     los ordena por día y por jaula, devolviendo un diccionario con el formato:
    
#         {
#           'dd/mm/YYYY': {
#               <jaula_id>: { ...data... },
#               <jaula_id2>: { ...data... },
#               ...
#           },
#           ...
#         }
    
#     @params:
#       - collection: Colección de MongoDB donde se almacenan los datos.
#       - center_id (str): ID de centro, en formato string, correspondiente al identificador único de cada centro.
#       - start_day (datetime): Fecha de inicio (incluida).
#       - end_day (datetime): Fecha final (exclusiva).
    
#     @returns:
#       - grouped_data (dict): Datos agrupados por día y por jaula.
#     """
#     try:

#         # Consulta para obtener todos los documentos del día de hoy
#         query = {
#             "timestamp": {"$gte": start_day, "$lt": end_day},
#             "centro_id": center_id
#         }
        
#         # Proyección para excluir el campo _id en los documentos retornados
#         projection = {"_id": 0}
        
#         # Se ordena la consulta por timestamp (ascendente o descendente según convenga)
#         # cursor = collection.find(query, projection).sort("timestamp", 1)
#         docs = list(collection.find(query, projection).sort("timestamp", 1))
            
#     except Exception as e:
#         data_analytic_logger(f"Error leyendo documentos de analítica {e.__traceback__.tb_lineno} : {e}")
#         return {}
    
#     #return list( cursor )
#     # Agrupar los documentos por día y por jaula.
#     grouped_data = {}
#     for doc in docs:
#         # Formatear la fecha según 'dd/mm/YYYY'
#         day = doc["timestamp"].strftime("%d/%m/%Y")
#         if day not in grouped_data:
#             grouped_data[day] = {}
#         # La clave 'camera_id' representa el identificador único o nombre de la jaula.
#         jaula_id = doc.get("camera_id")
#         grouped_data[day][jaula_id] = doc.get("data", {})

#     return grouped_data
    
def save_analytics_data( collection, docs_array, saved_days ):
    """ ### ? Almacena los datos de analítica en la coleccion correspondiente
     @params
     - docs_array [docs]  : Array de documentos a añadir en la colección, { timestamp, camera_id, data }
        - data: one_day_data (dict): Diccionario de los datos del día:
            { jaula: { sfr_value, delivered_food, tasa_units, tasa_values,  tasa_data, fasts_count, feeding_time_range, alarms_data, alarms_count } }
    """ 
    collection.insert_many(docs_array)
    info = f"{len(saved_days)} días" if len(saved_days)>1 else f"{len(saved_days)} día"
    info_jaula = f"{len(docs_array)} jaulas." if len(docs_array)>1 else f"{len(docs_array)} jaula."
    data_analytic_logger(f"Almacenado correctamente datos de {info}, con un total de {info_jaula}")
    

def ensure_index(collection, index_spec=[("camera_id", 1), ("timestamp", 1)]): #, index_name=None):
    """
    Revisa si el índice especificado en index_spec ya existe en la colección.
    Si existe, muestra su configuración. En caso contrario, crea el índice.

    :param collection: Instancia de la colección de MongoDB.
    :param index_spec: Diccionario con la especificación del índice (ej.: {"timestamp": 1}).
    :param index_name: Nombre opcional para el índice.
    :return: Nombre del índice existente o creado.
    """
    # Recorre todos los índices existentes
    for index in collection.list_indexes():
        if index["key"] == index_spec:
            # data_analytic_logger(f"El índice ya existe: {index}")
            return #  index["name"]

    # Si el índice no existe, se crea
    created_index = collection.create_index(index_spec ) #, name=index_name)
    data_analytic_logger(f"Índice creado: {created_index}")
    return # created_index

## Funciones para analítica de area ##

def fetch_analytic_area_data( collection, area, start_day, end_day):
    """ Obtiene los documentos de analítica almacenados en la base de datos para un rango de días y
    los ordena por día y por jaula, devolviendo un diccionario con el formato:
    
        {
          'dd/mm/YYYY': {
              <jaula_id>: { ...data... },
              <jaula_id2>: { ...data... },
              ...
          },
          ...
        }
    
    @params:
      - collection: Colección de MongoDB donde se almacenan los datos.
      - center_id (str): ID de centro, en formato string, correspondiente al identificador único de cada centro.
      - start_day (datetime): Fecha de inicio (incluida).
      - end_day (datetime): Fecha final (exclusiva).
    
    @returns:
      - grouped_data (dict): Datos agrupados por día y por jaula.
    """
    try:

        docs = get_docs_by_date_and_area(collection, area, start_day, end_day) # Por_area
        # docs_region = obtener_docs_por_fecha_y_region(URI, DATABASE, COLL, start_day, area, end_day)
            
    except Exception as e:
        data_analytic_logger(f"Error leyendo documentos de analítica {e.__traceback__.tb_lineno} : {e}")
        return {}
    
    # Agrupar los documentos por día y por jaula.
    grouped_data = {}
    for doc in docs:
        # Formatear la fecha según 'dd/mm/YYYY'
        day = doc["timestamp"].strftime("%d/%m/%Y")
        if day not in grouped_data:
            grouped_data[day] = {}
        # La clave 'camera_id' representa el identificador único o nombre de la jaula.
        jaula_id = doc.get("camera_id")
        centro_id = doc.get("centro_id")
        # print(f"{centro_id} \n")
        object_id = f"{centro_id}-{jaula_id}"
        data_to_pack = doc.get("data", {})
        # print(f"{data_to_pack} \n")
        data_to_pack.update({"centro_id":centro_id})
        grouped_data[day][object_id] = data_to_pack
    return grouped_data

def get_docs_by_date_and_area(
    collection: str,
    prefijo_objetivo: str,  # p.ej. "Loslagos-Puertomontt"
    start_date,         # ahora formato "DD-MM-YYYY", p.ej. "10-05-2025"
    end_date           # número de días desde la fecha inicial a solicitar
):
    """
    Conecta a MongoDB y devuelve los docs cuyo campo 'timestamp' caiga
    dentro del rango de fechas indicado y cuyo 'centro_id' coincida en
    los dos primeros segmentos con prefijo_objetivo.
    """

    # Parsear fecha_str a datetime
    # fecha_inicial = datetime.strptime(fecha_str, "%d-%m-%Y")
    fecha_inicial = start_date

    # Calcular fecha final sumando num_dias a fecha_inicial
    fecha_final = end_date
    # fecha_final = fecha_inicial + timedelta(days=num_dias)

    pipeline = [
        # 1) Filtrar por timestamp dentro del rango de fechas
        {"$match": {
            "timestamp": {
                "$gte": fecha_inicial,
                "$lt": fecha_final
            }
        }},

        # 2) Filtrar por los dos primeros segmentos de centro_id
        {"$match": {
            "$expr": {
                "$eq": [
                    {"$concat": [
                        {"$arrayElemAt": [
                            {"$split": ["$centro_id", "-"]}, 0
                        ]},
                        "-",
                        {"$arrayElemAt": [
                            {"$split": ["$centro_id", "-"]}, 1
                        ]}
                    ]},
                    prefijo_objetivo
                ]
            }
        }}
    ]

    return list(collection.aggregate(pipeline))

## FUNCIONES PARA REGION ##

def get_docs_by_region(
    collection,
    region_prefijo: str,  # Ej: "Loslagos"
    start_date,  # datetime.datetime
    end_date      # datetime.datetime
):
    """
    Devuelve documentos cuyo campo 'centro_id' comienza con 'region_prefijo'
    y cuyo 'timestamp' está dentro del rango especificado.
    """

    pipeline = [
        # 1) Filtrar por rango de fechas
        {"$match": {
            "timestamp": {
                "$gte": start_date,
                "$lt": end_date
            }
        }},
        # 2) Filtrar por prefijo de región usando regex
        {"$match": {
            "centro_id": {"$regex": f"^{region_prefijo}-"}
        }}
    ]
    result = list(collection.aggregate(pipeline))
    # print(len(result))
    return result


def fetch_analytic_region_data(docs):
    try:
        docs = docs
    except Exception as e:
        data_analytic_logger(f"Error leyendo documentos de analítica {e.__traceback__.tb_lineno} : {e}")
        return {}
    
    # Agrupar los documentos por día y por jaula.
    grouped_data = {}
    for doc in docs:
        # Formatear la fecha según 'dd/mm/YYYY'
        day = doc["timestamp"].strftime("%d/%m/%Y")
        if day not in grouped_data:
            grouped_data[day] = {}
        # La clave 'camera_id' representa el identificador único o nombre de la jaula.
        jaula_id = doc.get("camera_id")
        centro_id = doc.get("centro_id")
        # print(f"{centro_id} \n")
        object_id = f"{centro_id}-{jaula_id}"
        data_to_pack = doc.get("data", {})
        # print(f"{data_to_pack} \n")
        data_to_pack.update({"centro_id":centro_id})
        grouped_data[day][object_id] = data_to_pack
    return grouped_data

## Funciones Auxiliares para mejora de código ##

def _init_value_summary():
    return {
        "alarms_by_ton": [], "alarms_count": [], "alarms_threshold": [],
        "delivered_food": [], "fasts_count": [], "perdidos_count": [],
        "sfr_value": [], "tasa_units": [], "visitas_count": []
    }

def _init_tasa_summary():
    # usamos set para tasas y acumulamos alarm luego
    return {
        "alta": {"alarm": 0, "tasas": set()},
        "media": {"alarm": 0, "tasas": set()},
        "baja": {"alarm": 0, "tasas": set()}
    }

def _postprocess_values(resumen):
    # convierte listas en sumas/promedios
    for key, vals in resumen.items():
        if key in ("sfr_value", "alarms_threshold"):
            resumen[key] = sum(vals) / len(vals) if vals else 0
        elif key == "tasa_units":
            resumen[key] = vals[0] if vals else None
        else:
            resumen[key] = validar_y_sumar(vals)
    return resumen

def _postprocess_tasa(ts_summary):
    for lvl, info in ts_summary.items():
        info["tasas"] = list(info["tasas"])
    return ts_summary

def validar_y_sumar(lista):
    try:
        # Intentar convertir todos los elementos a enteros
        numeros = [int(x) for x in lista]
        return sum(numeros)
    except ValueError:
        try:
            # Si falla como enteros, intentar convertir todos a flotantes
            numeros = [float(x) for x in lista]
            return sum(numeros)
        except ValueError:
            # Si tampoco se pueden convertir a flotantes, lanzar error
            # raise ValueError("La lista contiene elementos no numéricos.")
            return None

""" Ejemplo de uso:
from datetime import datetime, timedelta

from scapp_database import fetch_data_collection, fetch_visitas_collection, data_analytic
from scapp_database.utils.analytic_database_utils import ensure_data_analytic_collection


today = datetime(2025,3,15)
start_day = datetime(today.year, today.month, today.day)
end_day = start_day + + timedelta(days=7)

load_data_analytic( data_analytic, start_day, end_day )

"""



""" Ejemplos de uso # ? 2 - fetch
import pprint
import sys
from datetime import datetime, timedelta
from scapp_database import fetch_data_collection
from scapp_database import fetch_data_collection, fetch_visitas_collection, data_analytic
from scapp_database.utils.analytic_database_utils import save_analytics_data, fetch_analytic_data

# today = datetime.now()  
today = datetime(2025,3,15)
start_day = datetime(today.year, today.month, today.day)
end_day     = start_day + timedelta(days=7+1)

loaded_data = fetch_analytic_data( data_analytic, start_day, end_day ) 


print(f"Obtenidos Peso de datos del día: {sys.getsizeof( analytic_data )} bytes demorando {(t_end - t1).total_seconds()*1e3} milisegundos")

"""





