"""CAP Discovery — Heartbeat loop and peer management.

Manages this agent's lifecycle in the cross-agent registry:
- Registers on startup
- Sends heartbeat every 15 seconds
- Cleans stale peers
- Deregisters on shutdown
"""

import asyncio
import logging
import os
from uuid import uuid4

from soagila.core.cap.registry import (
    register_agent,
    deregister_agent,
    heartbeat,
    get_peers,
    get_agent_info,
    cleanup_stale,
)

logger = logging.getLogger("soagila.cap.discovery")

HEARTBEAT_INTERVAL = 15  # seconds

# Module-level state
_agent_id: str | None = None
_auth_token: str | None = None
_product_type: str | None = None
_project_hash: str | None = None
_heartbeat_task: asyncio.Task | None = None


def get_current_agent_id() -> str | None:
    """Get this agent's ID."""
    return _agent_id


def get_current_auth_token() -> str | None:
    """Get this agent's auth token."""
    return _auth_token


def get_current_product_type() -> str | None:
    """Get this agent's product type."""
    return _product_type


def get_current_project_hash() -> str | None:
    """Get this agent's project hash."""
    return _project_hash


async def start_discovery(
    product_type: str,
    port: int,
    project_hash: str,
    project_name: str = "",
    capabilities: list[str] | None = None,
    version: str = "",
) -> str:
    """Register this agent and start the heartbeat loop. Returns auth_token."""
    global _agent_id, _auth_token, _product_type, _project_hash, _heartbeat_task

    _agent_id = f"{product_type}-{uuid4().hex[:8]}"
    _product_type = product_type
    _project_hash = project_hash

    _auth_token = await register_agent(
        agent_id=_agent_id,
        product_type=product_type,
        pid=os.getpid(),
        port=port,
        project_hash=project_hash,
        project_name=project_name,
        capabilities=capabilities,
        version=version,
    )

    # Start heartbeat loop
    _heartbeat_task = asyncio.create_task(_heartbeat_loop())
    logger.info(f"Discovery started: {_agent_id} (port {port})")

    return _auth_token


async def stop_discovery() -> None:
    """Deregister this agent and stop the heartbeat loop."""
    global _agent_id, _auth_token, _heartbeat_task

    if _heartbeat_task and not _heartbeat_task.done():
        _heartbeat_task.cancel()
        try:
            await _heartbeat_task
        except asyncio.CancelledError:
            pass
        _heartbeat_task = None

    if _agent_id:
        try:
            await deregister_agent(_agent_id)
        except Exception as e:
            logger.warning(f"Failed to deregister: {e}")
        _agent_id = None
        _auth_token = None

    logger.info("Discovery stopped")


async def get_peer_agents(same_project_only: bool = True) -> list[dict]:
    """Get live peer agents from the registry."""
    ph = _project_hash if same_project_only else None
    return await get_peers(project_hash=ph, exclude_agent_id=_agent_id)


async def get_all_agents(same_project_only: bool = True) -> list[dict]:
    """Get all agents including self."""
    ph = _project_hash if same_project_only else None
    return await get_peers(project_hash=ph, exclude_agent_id=None)


async def get_self_info() -> dict | None:
    """Get this agent's registry info."""
    if not _agent_id:
        return None
    return await get_agent_info(_agent_id)


async def _heartbeat_loop():
    """Background loop: heartbeat + stale cleanup."""
    while True:
        try:
            await asyncio.sleep(HEARTBEAT_INTERVAL)

            if _agent_id:
                await heartbeat(_agent_id)

            # Cleanup stale agents every 4th heartbeat (~60s)
            await cleanup_stale(max_age_seconds=60)

        except asyncio.CancelledError:
            raise
        except Exception as e:
            logger.warning(f"Heartbeat error: {e}")
