# Librerías python

# Librerías de terceros
from datetime import datetime

# Imports locales
from .clase_estacion import Estacion 

class Ambiental():
    """
        Clase que controla múltiples estaciones en un centro
    """
    
    def __init__( self, mongo_access ):
        """_summary_

        Args:
            mongo_access ( mongoDB DB ) : Acceso a base de datos Mongo, debe utilizarse para ingresar a la colección específica
            emit ( websocket emit )     : Función emit
            
        """
        self.mongo_access = mongo_access
        self.Nombres_Estaciones = []
        self.Estaciones = {}
        
    # ! -- Receive new Data -- #     
    def add_data_to_estacion( self, data_json ):
        # Tipos de datos:
        # data_json         : json con datos recibido por HTTP 
        
        # Obtener ID del dispositivo
        device_id       =   str( data_json['uplink_message']['decoded_payload']['idx'] )  
        
        # Si no hay registro local de la Estación
        if device_id not in self.Nombres_Estaciones:
            
            # Guardar id de etacion activa en lista local
            self.Nombres_Estaciones.append( device_id )
            
            # Iniciar clase Estacion
            self.Estaciones[ device_id ] = Estacion( name = device_id, db_collection = self.mongo_access[device_id] )

        # Añadir datos a la Estación    
        self.Estaciones[ device_id ].update_received_data( data_json )
        
        # Enviar nuevos datos al front
        updated = self._update_dict_to_send()
        return updated, device_id
        
        
    def _update_dict_to_send( self ):
        # Activada "manualmente" cada vez que se recibe un dato
        """ # array_estaciones: [
        #     {
        #       Nombre          : '0'   | str
        #       Batería         : 20    | int
        #       Profundidad_1   : {
        #                           O2 : {data , time}, 
        #                           DO2, Salinity, Temperature
        #                       },
        #       Profundidad_2   : {
        #                           O2 , DO2, Salinity, Temperature
        #                       },
        #       Profundidad_3   : {
        #                           O2 , DO2, Salinity, Temperature
        #                       },
        #     } , {...} , ...
        # ]
        
        #
        # { "time": formatted_time , "value": int( (newData['uplink_message']['decoded_payload']['doTemperature']) )} 
        """
        
        valid_send = []
        # array_estaciones = []
        
        try:
            
            time = datetime.now().strftime("%d-%m-%Y %H:%M:%S")
            
            for estacion in self.Estaciones.values(): 
                last_data  = estacion.latest_sensors_data_dict
                
                # Comprobar si la Estación ha recibido datos desde el último envío al servidor
                if estacion.new_data:
                    flag        = last_data.get('flags')  
                    estacion.new_data = False
                    
                else: 
                    flag      = '0000000000000000'
                
                if flag is None:
                    print(f'flag es None: {estacion.data_dict.keys()}')    
                    continue
                
                # Omitir Estación si no se han actualizado sus sensores
                if not '1' in flag: 
                    print( f'Se omite Estación {estacion.name}, no ha recibido datos de sensores desde último post a servidor' )
                    continue 
                
                valid_send.append(True)
                
                dict_to_send = self._create_dict_to_send( estacion, time, last_data )
                
                # array_estaciones.append( dict_to_send )
                
                # dict_to_send['fecha'] = time              # Descomentar si se utiliza array_estaciones
                
                # ? Actualizar dict_to_send
                estacion.update_server_data_dict( dict_to_send )
            
            
        except Exception as e:
            print(f"Error sending Datos estaciones at line {e.__traceback__.tb_lineno}: \n{e}\n")
        
        return any( valid_send )
    
    def _create_dict_to_send( self, estacion, time, last_data ):
        """
        Crea un diccionario con los datos a enviar para una estación específica.
        
        :param estacion: Objeto Estacion con datos de la estación.
        :param time: String con la hora y fecha actual.
        :param last_data: Diccionario con los últimos datos de la estación.
        :param flag: String con el estado de los datos.
        :return: Diccionario con los datos estructurados para enviar.
        """
        _profs = [self._create_prof_data( last_data, time, i ) for i in range(3)]

        return {
            'Nombre': estacion.name,
            'Battery': last_data.get('batteryPercent'),
            'fecha': time,  # Comentar si se utiliza array_estaciones
            'Profundidad_1': _profs[0],
            'Profundidad_2': _profs[1],
            'Profundidad_3': _profs[2]
        }
    
    def _create_prof_data( self, last_data, time, i ):
        """Create data for a specific depth."""
        sensor_names = [f'do{i+1}MgPerL', f'do{i+1}Percent', f'prof{i+1}Temperature', f'salinity{i+1}Ppt' , ]
        front_names = ['O2', 'DO2', 'Temp', 'Sal' ]
        sensor_data_dict = {front_names[idx]: self._fetch_sensor_data(last_data, name, time) for idx, name in enumerate(sensor_names)}
        # Añadir profundidad a cada dato enviado, en caso de existir el dato
        try: sensor_data_dict['profundidad'] = last_data['arrayProfs'][i]
        except: pass    
        
        return sensor_data_dict
    
    
    def _fetch_sensor_data( self, last_data, sensor_name, time ):
        """Fetch and format sensor data."""
        data = last_data.get(sensor_name)
        # print(f'{sensor_name} : {data} ')
        if data:
            return {'time': time, 'data': data}
        else: return None
        
        
     # ! -- Remote Server Data -- #    
   
    def get_data_for_cloud_server( self, estacion_idx ):
        
        all_sensors_data = []
        estacion = self.Estaciones[ estacion_idx ]
        try:
            if not estacion.cloud_data: 
                print(f"Cloud data de {estacion['name']} es False")
                return all_sensors_data
            
            sensors_data = estacion.server_data_dict()
            if len( sensors_data.keys() ) <= 3: return all_sensors_data
            
            sensors_data['Nombre'] = estacion.name          # Agrega el nombre de la estacion
            all_sensors_data.append( sensors_data )
                
        except Exception as e:
            print(f"Error obteniendo datos de estación: clase_ambiental línea {e.__traceback__.tb_lineno}: \n{e}\n")


        """ for estacion in self.Estaciones.values(): 
            try:
                if not estacion.cloud_data: 
                    print(f"Cloud data de {estacion['name']} es False")
                    continue
                
                sensors_data = estacion.server_data_dict()
                if len( sensors_data.keys() ) <= 3: continue
                
                sensors_data['Nombre'] = estacion.name          # Agrega el nombre de la estacion
                all_sensors_data.append( sensors_data )
                    
            except Exception as e:
                print(f"Error obteniendo datos de estación: clase_ambiental línea {e.__traceback__.tb_lineno}: \n{e}\n") """
        
        return all_sensors_data    
        
    
    
    
    
    
    # ? -- Fake Data -- #
    def send_random_data( self ):
        # Genera datos tal como llegarían desde Rodrigo y los va agregando a su Estacion
        import random
        from time import sleep
        
        while True:
            try:
                sleep(5)
                doMgPerL = round( 30*random.random() )
                temp = int( 20*random.random() ) 
                data = {
                    'end_device_ids' : {'device_id' : 'rak4631-pruebas-iniciales'} ,
                    'uplink_message' : { 'decoded_payload' : { 'Flags' : 255 , 'battery' : round( 100*random.random() , 0 ) , 
                                                            'doMgPerL' : doMgPerL , 'doPercent' : doMgPerL/100 , 
                                                            'doTemperature' : temp + round(random.random()*2 - 1 , 2) , 
                                                            'salinityPpt' : round( random.random()*20 , 2 ) , 
                                                            'salinityTemperature' : temp + round(random.random()*2 - 1 , 2) 
                                                            } , 
                                        
                                        'rx_metadata' : [{'time' : str( str(datetime.now()).split('.')[0].split(' ')[0] + 'T' + str(datetime.now()).split('.')[0].split(' ')[1] + '.51531'   )  }] },
                }
                
            except Exception as e:
                print(f'Error generando datos: \n{e}\n')
                continue
            
            # Guardado y envío de datos 
            self.add_dataEstacion( data )
            
            # if data['end_device_ids']['device_id'] not in self.Nombres_Estaciones:
            #     self.Nombres_Estaciones.append( data['end_device_ids']['device_id'] )
            #     self.Estaciones[ data['end_device_ids']['device_id'] ] = Estacion( data['end_device_ids']['device_id'] )
            
            # self.Estaciones[ data['end_device_ids']['device_id'] ].Add_Data( data )    

            # Send data
            #self.send_all_data()
                
    def send_fixed_data( self ):
        # Envía datos estáticos
        from datetime import datetime
        import random
        from time import sleep
        # DATA:
        temp_data = [
            "10,085", "10,085", "10,05", "10,08", "10,045", "10,04", "10,025", "10,18657143", "10,04428571", "10,062",
            "10,11162791", "10,005", "10,07295455", "10,01772727", "10,115", "10,065", "10,065", "10,055", "10,025", "10,07",
            "10,02", "10,12266667", "10,09136364", "10,07466667", "10,04071429", "10,07568182", "10,1187234", "10,09935897",
            "10,09", "10,18", "10,19", "10,2", "10,13", "10,15", "10,15", "10,39527273", "10,41977273", "10,39540541",
            "10,37180328", "10,34630435", "10,35465116", "10,34875", "10,08", "10,14", "10,37129032", "10,35727273", "10,065",
            "10,125", "10,09822785"
        ]

        DO_data = [
            "7,57", "7,43", "7,305", "7,475", "7,39", "7,225", "7,155", "8,264857143", "8,096428571", "7,796",
            "7,822093023", "7,687727273", "7,726136364", "7,611590909", "7,56", "7,33", "7,345", "7,31", "7,295", "7,6",
            "7,51", "7,79", "8,347045455", "8,156222222", "7,648095238", "7,742272727", "7,836382979", "7,760641026", "7,83",
            "8,29", "8,26", "8,24", "8,37", "8,36", "8,47", "8,346363636", "8,2975", "8,691351351", "8,559672131",
            "8,618695652", "8,626976744", "8,632", "7,75", "8,4", "8,6", "8,3", "8,4", "8,2", "8,5"
        ]

        DO_per_data = [
            "82,49", "80,905", "79,465", "81,355", "80,395", "78,585", "77,74", "90,08314286", "88,08785714", "84,855",
            "85,06744186", "83,76022727", "84,00295455", "82,90772727", "82,295", "79,75", "79,97", "79,525", "79,275",
            "82,725", "81,73", "85,05681818", "90,85977273", "88,73266667", "83,23952381", "84,31522727", "85,29191489",
            "84,44576923", "85,18", "90,35", "89,99", "90,03", "91,34", "91,2", "92,27", "91,48236364", "91,00090909",
            "95,26540541", "93,75754098", "94,37891304", "94,46906977", "94,51525", "85", "91", "96,02", "90,6", "91,2",
            "93,25", "94,25"
        ]

        # Convertir las cadenas con comas en flotantes
        temp_list = [float(item.replace(",", ".")) for item in temp_data]
        DO_list = [float(item.replace(",", ".")) for item in DO_data]
        DO_per_list = [float(item.replace(",", ".")) for item in DO_per_data]

        for i in range( len( temp_list ) ):
            try:
                sleep(0.5)
                do = DO_list[i]
                doMgPerL = DO_per_list[i]
                temp = temp_list[i]
                data = {
                    'end_device_ids' : {'device_id' : 'rak4631-pruebas-iniciales'} ,
                    'uplink_message' : { 'decoded_payload' : { 'Flags' : 255 , 'battery' : round( 100*random.random() , 0 ) , 
                                                            'doMgPerL' : do , 
                                                            'doPercent' : doMgPerL , 
                                                            'doTemperature' : temp  , 
                                                            'salinityPpt' : 30 + round(random.random()*0.05 - 0.025 , 2) , 
                                                            'salinityTemperature' : temp + round(random.random()*0.05 - 0.025 , 2)
                                                            } , 
                                        
                                        'rx_metadata' : [{'time' : str( str(datetime.now()).split('.')[0].split(' ')[0] + 'T' + str(datetime.now()).split('.')[0].split(' ')[1] + '.51531'   )  }] },
                }
                
            except Exception as e:
                print(f'Error generando datos: \n{e}\n')
                continue
            
            # Guardado y envío de datos 
            self.add_data_to_estacion( data )
    
   
        

    

















""" Json Ejemplo:
{
  "end_device_ids": {
    "device_id": "rak4631-pruebas-iniciales",
    "application_ids": {
      "application_id": "pruebas-itg"
    },
    "dev_eui": "AC1F09FFFE056E69",
    "join_eui": "AC1F09FFFE056E69",
    "dev_addr": "260D043A"
  },
  "correlation_ids": [
    "as:up:01GTA2Z4N8CNXZXD0J3PBH43AT",
    "gs:conn:01GT9MFAEPXTWFSN47E716C26R",
    "gs:up:host:01GT9MFAPWD26JNEAASS7C295S",
    "gs:uplink:01GTA2Z4ET5K8D85RS9YDQR2M4",
    "ns:uplink:01GTA2Z4EVNTC92MKA7KW08CSH",
    "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01GTA2Z4EVVAJGDBZ0WAZRY2T7",
    "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01GTA2Z4N8Q4G9AWFFVX8GP1PQ"
  ],
  "received_at": "2023-02-27T18:43:11.144507502Z",
  "uplink_message": {
    "session_key_id": "AYaUH6jJHWiSyG81I+VICw==",
    "f_port": 2,
    "f_cnt": 3,
    "frm_payload": "AUEAAKBBAAD6QQAAoEEAAMhCpHARQf8=",
    "decoded_payload": {
      "Flags": 255,
      "battery": 65,
      "doMgPerL": 100,
      "doPercent": 9.09000015258789,
      "doTemperature": 20,
      "idx": 1,
      "payload": [
        1,
        65,
        0,
        0,
        160,
        65,
        0,
        0,
        250,
        65,
        0,
        0,
        160,
        65,
        0,
        0,
        200,
        66,
        164,
        112,
        17,
        65,
        255
      ],
      "salinityPpt": 31.25,
      "salinityTemperature": 20
    },
    "rx_metadata": [
      {
        "gateway_ids": {
          "gateway_id": "eui-a84041fdfe24068f",
          "eui": "A84041FDFE24068F"
        },
        "time": "2023-02-27T18:43:10.772758Z",
        "timestamp": 1489467418,
        "rssi": -26,
        "channel_rssi": -26,
        "snr": 14.5,
        "frequency_offset": "-975",
        "uplink_token": "CiIKIAoUZXVpLWE4NDA0MWZkZmUyNDA2OGYSCKhAQf3+JAaPEJrwncYFGgwIvvPznwYQvpLEvwMgkMu02ayiAw==",
        "received_at": "2023-02-27T18:43:10.764670512Z"
      }
    ],
    "settings": {
      "data_rate": {
        "lora": {
          "bandwidth": 125000,
          "spreading_factor": 7,
          "coding_rate": "4/5"
        }
      },
      "frequency": "916800000",
      "timestamp": 1489467418,
      "time": "2023-02-27T18:43:10.772758Z"
    },
    "received_at": "2023-02-27T18:43:10.939716606Z",
    "confirmed": True,
    "consumed_airtime": "0.077056s",
    "network_ids": {
      "net_id": "000013",
      "tenant_id": "ttn",
      "cluster_id": "au1",
      "cluster_address": "au1.cloud.thethings.network"
    }
  }
}
"""
    