from datetime import datetime, timezone
from flask import Flask, request, jsonify, g
from pymongo import MongoClient, ReplaceOne
from bson import json_util  # viene con pymongo
# import numpy as np
import json
import numpy as np

from shared_db_utils import ensure_analytic_collection


app = Flask(__name__)

user_database_name = "users"
enviro_database_name = "ENVIRO"
nanoox_database_name =  "NANOOX__TS" #"ACOPIO_NANOOX"
scapp_database_name = "ScappDB"

def get_db():
    """Obtiene una conexión de MongoDB por request."""
    if 'db_client' not in g:
        g.db_client = MongoClient("mongodb://localhost:27017/")
    return g.db_client

# Ejemplo de conexión a MongoDB local
# client = MongoClient("mongodb://localhost:27017/")
# db = client.mi_base
# ? Server ping - usado por cliente backend
@app.route( '/ping', methods=['GET'] )
def ping():

    print("pong")
    return jsonify({"message": "pong"})

# ? Test de comunicación FE
@app.route('/check_server_status', methods=['HEAD'])
def check_server_status():
    return ('', 200)

@app.route('/scapp/mirror_analytics/<REGION>/<AREA>/<CENTRO>', methods=['POST'] )
def scapp_analytics_saving(CENTRO,AREA,REGION):
    CENTRO = CENTRO.capitalize()
    AREA = AREA.capitalize()
    REGION = REGION.capitalize()
    centro_id = f'{REGION}-{AREA}-{CENTRO}'
    received_json = request.json
    # raw_json = json.dumps(received_json)
    # proccess_json = json_util.loads(json.dumps(received_json))
    proccess_json = json.loads(received_json, object_hook=json_util.object_hook)
    # print(f"{received_json=} \n")
    
    print(f"{proccess_json=} \n")
    
    try:
        client = get_db()
        scapp_db = client[scapp_database_name]

        bulk_scapp_analytics(scapp_db, proccess_json, centro_id)
        
        print(f"Se ha guardado exitosamente la anaĺitica Scapp del centro {centro_id}")
        return jsonify({"mensaje": "Datos de analítica Scapp recibidos exitosamente por servidor"}), 200
    except Exception as e:
        print(f"No se ha guardado correctamente a anaĺitica Scapp del centro {centro_id} por error en línea {e.__traceback__.tb_lineno}: \n{e}")
        return jsonify({"mensaje": "Datos recibidos erroneamente por servidor"}), 401

def bulk_scapp_analytics(scapp_db, received_json, centro_id):
    """
    Acepta received_json como dict o lista de dicts.  
    Normaliza y reemplaza cada documento por centro, cámara y día.
    """
    coll = ensure_analytic_collection(scapp_db)
    # Normalizamos a lista
    docs = []
    if isinstance(received_json, dict):
        docs = [received_json]
    elif isinstance(received_json, (list, np.ndarray)):
        docs = received_json
    else:
        raise ValueError("received_json debe ser dict o lista de dicts")

    ops = []
    for doc_in in docs:
        ts  = doc_in.get("timestamp")
        cam = doc_in.get("camera_id")
        if not isinstance(ts, datetime):
            try:
                ts = datetime.fromisoformat(ts)
            except Exception as e:
                raise ValueError(f"'timestamp' inválido en documento {ts}") from e
        # if not isinstance(ts, datetime):
        #     print(f"Convirtiendo {ts} a datetime")
        #     ts = datetime.fromisoformat(ts)
        #     if not isinstance(ts, datetime):
        #         raise ValueError("El campo 'timestamp' debe ser un datetime en cada documento")
        if cam is None:
            raise ValueError("Falta 'camera_id' en alguno de los documentos")

        tz        = ts.tzinfo or timezone.utc
        start_day = datetime(ts.year, ts.month, ts.day, tzinfo=tz)

        filtro = {
            "centro_id": centro_id,
            "camera_id": cam,
            "timestamp": start_day,
        }

        doc = {"centro_id": centro_id}
        doc.update({k: v for k, v in doc_in.items() if k != "_id"})
        doc["timestamp"] = start_day

        ops.append(ReplaceOne(filtro, doc, upsert=True))

    if ops:
        result = coll.bulk_write(ops, ordered=True)
        print(f"→ Modificados: {result.modified_count}, Insertados: {result.upserted_count}")


@app.teardown_appcontext
def close_db(exc=None):
    """Cierra la conexión a MongoDB después de cada request."""
    db_client = g.pop('db_client', None)  # Extrae y elimina la conexión del contexto global
    if db_client is not None:
        db_client.close()  # Cierra la conexión solo si existía


if __name__ == "__main__":
    # Al lanzar desde Jupyter, pondrá en marcha el servidor y bloqueará la celda.
    host = '0.0.0.0'
    port = 4024
    debug=False
    print("Service Manager para recepción Analitica Scapp")    
    # app.run(host="0.0.0.0", port=7777, debug=False)
    with app.app_context():
        app.run(host=host, port=port, debug=debug)
