from fastapi import FastAPI, HTTPException, status, APIRouter 
from typing import List, Any, Optional
from pymongo import MongoClient
from bson.objectid import ObjectId
from dotenv import load_dotenv
from pydantic import BaseModel, Field, ConfigDict
from pymongo.errors import ServerSelectionTimeoutError
from datetime import datetime
from zoneinfo import ZoneInfo

import os
from datetime import datetime
from zoneinfo import ZoneInfo
import uvicorn 

load_dotenv()

# Zona horaria de Guayaquil, Ecuador
ECUADOR_TZ = ZoneInfo("America/Guayaquil")

def get_current_date_in_format_day_only():
    now = datetime.now(ECUADOR_TZ)
    return now.strftime("%A %#d de %b de %Y").replace('.', '').capitalize()

def get_current_time_in_format_hour_only():
    now = datetime.now(ECUADOR_TZ)
    return now.strftime("%H:%M")

# --- MODELOS (Sin Cambios) ---
class MCPTransportista(BaseModel):
    whatsapp: str
    nombre: str

class MCPVehiculo(BaseModel):
    placa: str
    carga_maxima: float

class RegistroTransportistaBody(BaseModel):
    transportista: MCPTransportista
    vehiculo: MCPVehiculo
    
class Vehiculo(BaseModel):
    placa: str = Field(..., description="Matrícula única del vehículo.")
    carga_maxima: float = Field(..., gt=0, description="Capacidad máxima de carga en toneladas.")

class MongoBaseModel(BaseModel):
    id: Optional[str] = Field(alias="_id", default=None)
    model_config = ConfigDict(
        populate_by_name=True,
        json_schema_extra={"example": {"id": "60d5ec49b14f6b3d4f4d4e3a"}}
    )

class Transportista(MongoBaseModel):
    nombre: str = Field(..., description="Nombre completo del transportista.")
    telefono_whatsapp: str = Field(..., description="Número de contacto principal (whatsapp).")
    vehiculos: List[Vehiculo] = Field(default_factory=list, description="Lista de vehículos asociados.")

class OrdenDeServicio(MongoBaseModel):
    whatsappCliente: str
    nombreCliente: str
    carga: str = Field(..., description="Descripción detallada de la carga.")
    peso_aproximado: Optional[float] = Field(None, description="Peso estimado de la carga.")
    origen: str
    destino: str
    estado: str = "pendiente"
    fecha: str = Field(default_factory=get_current_date_in_format_day_only)
    hora: str = Field(default_factory=get_current_time_in_format_hour_only)
    
# --- CONFIGURACIÓN DE FASTAPI Y CONEXIÓN A MONGO ---

# 🌟 1. Se añade el root_path a la aplicación principal de FastAPI
app = FastAPI(title="API MOVILIZIA", root_path="/api-movilizia")
router = APIRouter() 

MONGO_URI = os.getenv("MONGO_URI")
MONGO_DB_NAME = os.getenv("MONGO_DB_NAME")

client: MongoClient = None
db: Any = None

@app.on_event("startup")
def startup_db_client():
    global client, db
    
    if not MONGO_URI or not MONGO_DB_NAME:
        raise ValueError("MONGO_URI y MONGO_DB_NAME deben estar definidos.")
    try:
        client = MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000) 
        client.admin.command('ping') 
        db = client[MONGO_DB_NAME]
        print(f"Conectado a MongoDB: {MONGO_DB_NAME}")
    except (ServerSelectionTimeoutError, Exception) as e: 
        print(f"ERROR FATAL AL CONECTAR A MONGO: {e}")
        db = None 

@app.on_event("shutdown")
def shutdown_db_client():
    global client
    if client:
        client.close()
        print("Conexión a MongoDB cerrada.")

# --- HELPERS (Sin Cambios) ---
def validate_object_id(id: str):
    try:
        return ObjectId(id)
    except Exception:
        raise HTTPException(status_code=400, detail="ID inválido")

# ======================================================================
# --- ENDPOINTS: TRANSPORTISTAS ---
# ======================================================================

@router.post(
    "/transportistas/register",
    response_model=Transportista,
    status_code=status.HTTP_201_CREATED,
    tags=["Transportistas"]
)
def register_transportista_and_vehicle_unified(request_body: RegistroTransportistaBody):
    """Registra un Transportista y su Vehículo."""
    global db
    if db is None:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Error al registrar: DB no activa.")
    
    nuevo_transportista = Transportista(
        nombre=request_body.transportista.nombre,
        telefono_whatsapp=request_body.transportista.whatsapp,
        vehiculos=[
            Vehiculo(placa=request_body.vehiculo.placa, carga_maxima=request_body.vehiculo.carga_maxima)
        ]
    )
    data_to_insert = nuevo_transportista.model_dump(by_alias=True, exclude_none=True)
    if 'id' in data_to_insert: data_to_insert.pop("id")
    
    try:
        result = db.transportistas.insert_one(data_to_insert)
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error DB: {e}")

    created = db.transportistas.find_one({"_id": result.inserted_id})
    if created and created.get('_id'): created['_id'] = str(created['_id'])
    return Transportista.model_validate(created, from_attributes=True)


@router.get("/transportistas/", response_model=List[Transportista], tags=["Transportistas"])
def read_transportistas():
    """Lee todos los transportistas."""
    global db
    if db is None:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Error al listar: DB no activa.")
        
    try:
        results = db.transportistas.find()
        validated_results = []
        for doc in results:
            if doc.get('_id'): doc['_id'] = str(doc['_id'])
            validated_results.append(Transportista.model_validate(doc, from_attributes=True))
        return validated_results
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error DB: {e}")

# ✅ NUEVO ENDPOINT: Buscar transportista por número de WhatsApp
@router.get("/transportistas/{whatsapp}", response_model=Transportista, tags=["Transportistas"])
def get_transportista_by_whatsapp(whatsapp: str):
    """Busca un transportista por su número de WhatsApp."""
    global db
    if db is None:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail="Error: DB no activa."
        )
    
    try:
        transportista = db.transportistas.find_one({"telefono_whatsapp": whatsapp})
        if not transportista:
            # Si no existe, lanzamos un 404 limpio
            raise HTTPException(
                status_code=404,
                detail=f"No se encontró transportista con WhatsApp {whatsapp}"
            )

        transportista["_id"] = str(transportista["_id"])
        return Transportista.model_validate(transportista, from_attributes=True)

    except HTTPException as e:
        # ⚙️ Propagamos los errores controlados (como 404)
        raise e
    except Exception as e:
        # 🔁 Solo los errores no controlados dan 400
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Error DB: {e}"
        )



# ✅ NUEVO ENDPOINT: Eliminar transportista por número de WhatsApp
@router.delete("/transportistas/{whatsapp}", status_code=status.HTTP_200_OK, tags=["Transportistas"])
def delete_transportista_by_whatsapp(whatsapp: str):
    """Elimina un transportista por su número de WhatsApp."""
    global db
    if db is None:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Error: DB no activa.")
    
    try:
        result = db.transportistas.delete_one({"telefono_whatsapp": whatsapp})
        if result.deleted_count == 0:
            raise HTTPException(status_code=404, detail=f"No se encontró transportista con WhatsApp {whatsapp}")
        return {"message": f"Transportista con WhatsApp {whatsapp} eliminado correctamente."}
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error DB: {e}")


# ======================================================================
# --- ENDPOINTS: ÓRDENES DE SERVICIO ---
# ======================================================================

@router.post(
    "/ordenes/", response_model=OrdenDeServicio, status_code=status.HTTP_201_CREATED, tags=["Órdenes de Servicio"]
)
def create_orden(orden_data: OrdenDeServicio):
    """Crea una nueva orden de servicio."""
    global db
    if db is None:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Error al crear: DB no activa.")
        
    data = orden_data.model_dump(by_alias=True, exclude_none=True)
    if 'id' in data: data.pop("id")
    
    try:
        result = db.ordenes.insert_one(data)
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error al crear orden en la DB: {e}")
        
    created = db.ordenes.find_one({"_id": result.inserted_id})
    if created and created.get('_id'): created['_id'] = str(created['_id'])
    return OrdenDeServicio.model_validate(created, from_attributes=True)

@router.get("/ordenes/", response_model=List[OrdenDeServicio], tags=["Órdenes de Servicio"])
def read_ordenes():
    """Lee todas las órdenes de servicio."""
    global db
    if db is None:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Error al listar: DB no activa.")
        
    try:
        results = db.ordenes.find()
        validated_results = []
        for doc in results:
            if doc.get('_id'): doc['_id'] = str(doc['_id'])
            validated_results.append(OrdenDeServicio.model_validate(doc, from_attributes=True))
        return validated_results
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error al listar órdenes: {e}")

@router.get("/ordenes/{orden_id}", response_model=OrdenDeServicio, tags=["Órdenes de Servicio"])
def get_orden_by_id(orden_id: str):
    """Busca una orden por su ID."""
    global db
    if db is None:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="DB no activa.")
    
    obj_id = validate_object_id(orden_id)
    
    orden = db.ordenes.find_one({"_id": obj_id})
    if not orden:
        raise HTTPException(status_code=404, detail=f"No se encontró la orden con ID {orden_id}")
    
    orden['_id'] = str(orden['_id'])
    return OrdenDeServicio.model_validate(orden, from_attributes=True)

    
# Modelo para actualizar orden + transportista
class UpdateOrdenTransportistaBody(BaseModel):
    id: str
    nuevo_estado: Optional[str] = None
    whatsapp_transportista: Optional[str] = None
    nombre_transportista: Optional[str] = None

@router.patch("/ordenes/actualizar", response_model=OrdenDeServicio, tags=["Órdenes de Servicio"])
def update_orden_y_transportista(request_body: UpdateOrdenTransportistaBody):
    """Actualiza el estado de la orden y los datos del transportista asociados."""
    global db
    if db is None:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="DB no activa.")

    obj_id = validate_object_id(request_body.id)

    try:
        # Buscar la orden
        orden = db.ordenes.find_one({"_id": obj_id})
        if not orden:
            raise HTTPException(status_code=404, detail=f"No se encontró la orden con ID {request_body.id}")

        update_data = {}
        if request_body.nuevo_estado:
            update_data["estado"] = request_body.nuevo_estado
        if request_body.whatsapp_transportista:
            update_data["whatsapp_transportista"] = request_body.whatsapp_transportista
        if request_body.nombre_transportista:
            update_data["nombre_transportista"] = request_body.nombre_transportista

        if update_data:
            db.ordenes.update_one({"_id": obj_id}, {"$set": update_data})

        # Obtener la orden actualizada
        orden_actualizada = db.ordenes.find_one({"_id": obj_id})
        if orden_actualizada and orden_actualizada.get('_id'):
            orden_actualizada['_id'] = str(orden_actualizada['_id'])

        return OrdenDeServicio.model_validate(orden_actualizada, from_attributes=True)

    except HTTPException as e:
        raise e
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Error al actualizar la orden: {e}")


# ======================================================================
# --- INCLUIR ROUTER EN LA APLICACIÓN PRINCIPAL ---
# ======================================================================

# CAMBIO CRÍTICO: No se utiliza el argumento 'prefix'
app.include_router(router, tags=["Transporte Rural"])

# ======================================================================
# --- EJECUCIÓN CON UVICORN ---
# ======================================================================

if __name__ == "__main__":
    uvicorn.run(
        app,
        host="0.0.0.0", 
        port=8002
    )