"""Lifecycle hook helpers for instrumenting tool instance transitions."""

import logging
import uuid
from datetime import datetime, timezone

from sqlalchemy.ext.asyncio import AsyncSession

from src.models import InstanceEvent
from src.models import ToolInstance
from src.services.shared.correlation import get_correlation_id
from src.services.instance.event_bus import InstanceEventBus, InstanceEventPayload
from src.services.shared.notification_service import notification_service

logger = logging.getLogger(__name__)


def _derive_title(event_type: str) -> str:
    """Map lifecycle event type to a human-readable notification title."""
    mapping = {
        "instance.created": "Container created",
        "instance.started": "Container started",
        "instance.stopped": "Container stopped",
        "instance.restarted": "Container restarted",
        "instance.deleted": "Container deleted",
        "instance.error": "Container error",
        "instance.health_changed": "Container ready",
    }
    return mapping.get(
        event_type,
        event_type.replace("instance.", "").replace("_", " ").title(),
    )


def _should_notify(event_type: str, status: str | None) -> bool:
    """Determine whether a lifecycle event should generate a notification.

    Only warnings, errors, and "container is ready" (health_changed running)
    are sent to users.
    """
    if event_type == "instance.error":
        return True
    if event_type == "instance.health_changed" and status == "running":
        return True
    # Filter out: created, started, stopped, restarted, deleted, and any
    # health_changed that is not "running" (unhealthy is handled by health_monitor)
    return False


def _build_payload(
    event_type: str,
    instance: ToolInstance,
    status: str | None = None,
    message: str | None = None,
    metadata: dict | None = None,
) -> InstanceEventPayload:
    """Construct a standard event payload."""
    return {
        "event": event_type,
        "instance_id": str(instance.id),
        "status": status or instance.status,
        "message": message,
        "metadata": metadata or {},
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "correlation_id": get_correlation_id(),
    }


async def _write_audit_row(
    session: AsyncSession,
    instance: ToolInstance,
    event_type: str,
    created_by: uuid.UUID | None = None,
    status: str | None = None,
    message: str | None = None,
    metadata: dict | None = None,
) -> InstanceEvent:
    """Persist an instance_events audit row."""
    row = InstanceEvent(
        instance_id=instance.id,
        event_type=event_type.replace("instance.", ""),
        status=status or instance.status,
        message=message,
        created_by=created_by,
        event_metadata=metadata or {},
    )
    session.add(row)
    await session.commit()
    return row


async def publish_lifecycle_event(
    event_bus: InstanceEventBus,
    session: AsyncSession,
    instance: ToolInstance,
    event_type: str,
    created_by: uuid.UUID | None = None,
    status: str | None = None,
    message: str | None = None,
    metadata: dict | None = None,
) -> None:
    """Publish a lifecycle event and write an audit row after DB commit.

    Args:
        event_bus: The global event bus.
        session: Active async DB session.
        instance: The affected tool instance.
        event_type: One of instance.created, instance.started, etc.
        created_by: User ID for user-initiated actions; None for system.
        status: Optional status override.
        message: Optional human-readable message.
        metadata: Optional extra metadata.
    """
    payload = _build_payload(
        event_type=event_type,
        instance=instance,
        status=status,
        message=message,
        metadata=metadata,
    )

    # Write audit row
    await _write_audit_row(
        session=session,
        instance=instance,
        event_type=event_type,
        created_by=created_by,
        status=status or instance.status,
        message=message,
        metadata=metadata,
    )

    # Publish to bus
    await event_bus.publish(event_type, payload)

    # Create notification for instance owner (fire-and-forget)
    # Only send warnings, errors, and "container is ready" notifications.
    effective_status = status or instance.status
    if not _should_notify(event_type, effective_status):
        return

    severity = "error" if event_type == "instance.error" else "success"
    title = _derive_title(event_type)

    try:
        await notification_service.create_notification(
            session=session,
            user_id=instance.owner_id,
            category="instance",
            severity=severity,
            title=title,
            message=message,
            source_type="tool_instances",
            source_id=instance.id,
            metadata=metadata,
        )
    except Exception:
        logger.exception(
            "Failed to create notification for lifecycle event %s",
            event_type,
            extra={"correlation_id": payload.get("correlation_id", "unknown")},
        )
