# #region SessionEventLoggerModule [C:4] [TYPE Module] [SEMANTICS sqlalchemy, dataset, event, logging, session]
# @BRIEF Persist explicit session mutation events for dataset-review audit trails without weakening ownership or approval invariants.
# @LAYER Domain
# @RELATION DEPENDS_ON -> [SessionEvent]
# @RELATION DEPENDS_ON -> [DatasetReviewSession]
# @PRE  Caller provides an owned session scope and an authenticated actor identifier for each persisted mutation event.
# @POST  Every logged event is committed as an explicit, queryable audit record with deterministic event metadata.
# @SIDE_EFFECT  Inserts persisted session event rows and emits runtime belief-state logs for audit-sensitive mutations.
# @DATA_CONTRACT  Input[SessionEventPayload] -> Output[SessionEvent]

from __future__ import annotations

# #region SessionEventLoggerImports [TYPE Block]
from dataclasses import dataclass, field
from typing import Any

from sqlalchemy.orm import Session

from src.core.logger import belief_scope, logger
from src.models.dataset_review import DatasetReviewSession, SessionEvent

# #endregion SessionEventLoggerImports


# #region SessionEventPayload [C:2] [TYPE Class]
# @BRIEF Typed input contract for one persisted dataset-review session audit event.
@dataclass(frozen=True)
class SessionEventPayload:
    session_id: str
    actor_user_id: str
    event_type: str
    event_summary: str
    current_phase: str | None = None
    readiness_state: str | None = None
    event_details: dict[str, Any] = field(default_factory=dict)
# #endregion SessionEventPayload


# #region SessionEventLogger [C:4] [TYPE Class]
# @BRIEF Persist explicit dataset-review session audit events with meaningful runtime reasoning logs.
# @RELATION DEPENDS_ON -> [SessionEvent]
# @RELATION DEPENDS_ON -> [SessionEventPayload]
# @PRE  The database session is live and payload identifiers are non-empty.
# @POST  Returns the committed session event row with a stable identifier and stored detail payload.
# @SIDE_EFFECT  Writes one audit row to persistence and emits logger.reason/logger.reflect traces.
# @DATA_CONTRACT  Input[SessionEventPayload] -> Output[SessionEvent]
class SessionEventLogger:
    # region SessionEventLogger_init [TYPE Function]
    # @PURPOSE: Bind a live SQLAlchemy session to the session-event logger.
    def __init__(self, db: Session) -> None:
        self.db = db
    # endregion SessionEventLogger_init

    # region log_event [TYPE Function]
    # @PURPOSE: Persist one explicit session event row for an owned dataset-review mutation.
    # @RELATION DEPENDS_ON ->[SessionEvent]
    # @PRE  session_id, actor_user_id, event_type, and event_summary are non-empty.
    # @POST  Returns the committed SessionEvent record with normalized detail payload.
    # @SIDE_EFFECT  Inserts and commits one session_events row.
    # @DATA_CONTRACT  Input[SessionEventPayload] -> Output[SessionEvent]
    def log_event(self, payload: SessionEventPayload) -> SessionEvent:
        with belief_scope("SessionEventLogger.log_event"):
            session_id = str(payload.session_id or "").strip()
            actor_user_id = str(payload.actor_user_id or "").strip()
            event_type = str(payload.event_type or "").strip()
            event_summary = str(payload.event_summary or "").strip()

            if not session_id:
                logger.explore("Session event logging rejected because session_id is empty")
                raise ValueError("session_id must be non-empty")
            if not actor_user_id:
                logger.explore(
                    "Session event logging rejected because actor_user_id is empty",
                    extra={"session_id": session_id},
                )
                raise ValueError("actor_user_id must be non-empty")
            if not event_type:
                logger.explore(
                    "Session event logging rejected because event_type is empty",
                    extra={"session_id": session_id, "actor_user_id": actor_user_id},
                )
                raise ValueError("event_type must be non-empty")
            if not event_summary:
                logger.explore(
                    "Session event logging rejected because event_summary is empty",
                    extra={"session_id": session_id, "event_type": event_type},
                )
                raise ValueError("event_summary must be non-empty")

            normalized_details = dict(payload.event_details or {})
            logger.reason(
                "Persisting explicit dataset-review session audit event",
                extra={
                    "session_id": session_id,
                    "actor_user_id": actor_user_id,
                    "event_type": event_type,
                    "current_phase": payload.current_phase,
                    "readiness_state": payload.readiness_state,
                },
            )

            event = SessionEvent(
                session_id=session_id,
                actor_user_id=actor_user_id,
                event_type=event_type,
                event_summary=event_summary,
                current_phase=payload.current_phase,
                readiness_state=payload.readiness_state,
                event_details=normalized_details,
            )
            self.db.add(event)
            self.db.commit()
            self.db.refresh(event)

            logger.reflect(
                "Dataset-review session audit event persisted",
                extra={
                    "session_id": session_id,
                    "session_event_id": event.session_event_id,
                    "event_type": event.event_type,
                },
            )
            return event
    # endregion log_event

    # region log_for_session [TYPE Function]
    # @PURPOSE: Convenience wrapper for logging an event directly from a session aggregate root.
    # @RELATION CALLS ->[EXT:method:SessionEventLogger.log_event]
    def log_for_session(
        self,
        session: DatasetReviewSession,
        *,
        actor_user_id: str,
        event_type: str,
        event_summary: str,
        event_details: dict[str, Any] | None = None,
    ) -> SessionEvent:
        return self.log_event(
            SessionEventPayload(
                session_id=session.session_id,
                actor_user_id=actor_user_id,
                event_type=event_type,
                event_summary=event_summary,
                current_phase=session.current_phase.value if session.current_phase else None,
                readiness_state=session.readiness_state.value if session.readiness_state else None,
                event_details=dict(event_details or {}),
            )
        )
    # endregion log_for_session
# #endregion SessionEventLogger

# #endregion SessionEventLoggerModule
