"""Endpoints de registro y gestion de Hardware Agents."""
import json
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session as DBSession
from datetime import datetime
from database import get_db
from models import Agent, SyncQueue
from schemas import AgentRegister, AgentOut, AgentWithToken
from config import REGISTERED_AGENTS, VENUE_ID
from auth import generate_token, require_agent_token


def _sync_agent_status(db: DBSession, agent: Agent):
    """Agrega un registro a la cola de sync para que el cloud conozca el estado del agente."""
    db.add(SyncQueue(
        table_name='agents',
        record_id=agent.id,
        action='status_update',
        payload=json.dumps({
            'id': agent.id,
            'name': agent.name,
            'status': agent.status,
            'last_heartbeat': agent.last_heartbeat.isoformat() if agent.last_heartbeat else None,
        }),
    ))

router = APIRouter(prefix="/api/agents", tags=["Agents"])


@router.post("/register", response_model=AgentWithToken)
def register_agent(req: AgentRegister, db: DBSession = Depends(get_db)):
    """Registra un Hardware Agent. Si ya existe, actualiza su host/port."""
    agent = db.query(Agent).filter(Agent.id == req.id).first()
    if agent:
        agent.host = req.host
        agent.port = req.port
        agent.name = req.name
        agent.status = 'online'
        agent.last_heartbeat = datetime.now()
        # Mantener token existente (no regenerar)
    else:
        agent = Agent(
            id=req.id,
            name=req.name,
            host=req.host,
            port=req.port,
            token=generate_token(),
            status='online',
            last_heartbeat=datetime.now(),
            venue_id=VENUE_ID,
        )
        db.add(agent)

    _sync_agent_status(db, agent)
    db.commit()
    db.refresh(agent)

    # Actualizar diccionario en memoria
    REGISTERED_AGENTS[agent.id] = {"host": agent.host, "port": agent.port}
    print(f"[Agent] Registrado: {agent.id} -> http://{agent.host}:{agent.port}")

    return agent


@router.delete("/{agent_id}")
def unregister_agent(
    agent_id: str,
    caller: Agent = Depends(require_agent_token),
    db: DBSession = Depends(get_db),
):
    """Desregistra un Hardware Agent. Requiere Bearer token del propio agent."""
    if caller.id != agent_id:
        raise HTTPException(status_code=403, detail="Solo puedes desregistrar tu propio agent")

    agent = db.query(Agent).filter(Agent.id == agent_id).first()
    if not agent:
        raise HTTPException(status_code=404, detail="Agent no encontrado")

    # Encolar sync de eliminacion ANTES de borrar
    db.add(SyncQueue(
        table_name='agents',
        record_id=agent.id,
        action='delete',
        payload=json.dumps({'id': agent.id, 'venue_id': VENUE_ID}),
    ))

    db.delete(agent)
    db.commit()

    # Quitar del diccionario en memoria
    REGISTERED_AGENTS.pop(agent_id, None)
    print(f"[Agent] Desregistrado: {agent_id}")

    return {"status": "ok", "message": f"Agent {agent_id} eliminado"}


@router.get("", response_model=list[AgentOut])
def list_agents(db: DBSession = Depends(get_db)):
    """Lista los agents registrados en este venue."""
    return db.query(Agent).filter(Agent.venue_id == VENUE_ID).all()


@router.post("/{agent_id}/heartbeat")
def heartbeat(
    agent_id: str,
    caller: Agent = Depends(require_agent_token),
    body: dict | None = None,
    db: DBSession = Depends(get_db),
):
    """Actualiza el heartbeat de un agent (keep-alive). Requiere Bearer token.
    Opcionalmente recibe {host, port} para actualizar IP dinamicamente."""
    if caller.id != agent_id:
        raise HTTPException(status_code=403, detail="Token no corresponde a este agent")

    agent = db.query(Agent).filter(Agent.id == agent_id).first()
    if not agent:
        raise HTTPException(status_code=404, detail="Agent no encontrado")

    # Actualizar IP si el agent la envia y cambio
    if body and body.get("host"):
        new_host = body["host"]
        new_port = body.get("port", agent.port)
        if agent.host != new_host or agent.port != new_port:
            print(f"[Agent] {agent_id} IP actualizada: {agent.host}:{agent.port} -> {new_host}:{new_port}")
            agent.host = new_host
            agent.port = new_port

    agent.status = 'online'
    agent.last_heartbeat = datetime.now()
    _sync_agent_status(db, agent)
    db.commit()

    # Asegurar que esta en memoria (con IP actualizada)
    REGISTERED_AGENTS[agent.id] = {"host": agent.host, "port": agent.port}

    return {"status": "ok", "agent_id": agent_id, "timestamp": agent.last_heartbeat.isoformat()}


@router.post("/{agent_id}/regenerate-token", response_model=AgentWithToken)
def regenerate_token(
    agent_id: str,
    caller: Agent = Depends(require_agent_token),
    db: DBSession = Depends(get_db),
):
    """Regenera el API token de un agent. Requiere Bearer token actual."""
    if caller.id != agent_id:
        raise HTTPException(status_code=403, detail="Solo puedes regenerar tu propio token")

    agent = db.query(Agent).filter(Agent.id == agent_id).first()
    if not agent:
        raise HTTPException(status_code=404, detail="Agent no encontrado")

    agent.token = generate_token()
    db.commit()
    db.refresh(agent)

    print(f"[Agent] Token regenerado para: {agent_id}")
    return agent
