# #region SessionRepositoryMutations [C:4] [TYPE Module] [SEMANTICS dataset, repository, mutation, session, persistence]
# @BRIEF Persistence mutation operations for dataset review session aggregates — profile/findings, recovery state, preview, run context.
# @LAYER Domain
# @RELATION DEPENDS_ON -> [DatasetReviewModels]
# @RELATION DEPENDS_ON -> [SessionEventLogger]
# @PRE  All mutations execute within authenticated request or task scope.
# @POST  Session aggregate writes preserve ownership and version semantics.

from __future__ import annotations

from typing import Any, cast

from sqlalchemy.orm import Session

from src.core.logger import belief_scope, logger
from src.models.dataset_review import (
    CompiledPreview,
    DatasetProfile,
    DatasetReviewSession,
    DatasetRunContext,
    ExecutionMapping,
    ImportedFilter,
    TemplateVariable,
    ValidationFinding,
)
from src.services.dataset_review.event_logger import SessionEventLogger

logger = cast(Any, logger)


# #region save_profile_and_findings [C:4] [TYPE Function]
# @BRIEF Persist profile state and replace validation findings for an owned session in one transaction.
# @PRE  session_id belongs to user_id and the supplied profile/findings belong to the same aggregate scope.
# @POST  stored profile matches the current session and findings are replaced by the supplied collection.
# @SIDE_EFFECT  updates profile rows, deletes stale findings, inserts current findings, and commits the transaction.
def save_profile_and_findings(
    db: Session,
    event_logger: SessionEventLogger,
    get_owned_session,
    require_session_version,
    commit_session_mutation,
    session_id: str,
    user_id: str,
    profile: DatasetProfile,
    findings: list[ValidationFinding],
    expected_version: int | None = None,
) -> DatasetReviewSession:
    with belief_scope("save_profile_and_findings"):
        session = get_owned_session(session_id, user_id)
        if expected_version is not None:
            require_session_version(session, expected_version)
        logger.reason("Persisting dataset profile and replacing validation findings", extra={"session_id": session_id, "user_id": user_id, "has_profile": bool(profile), "findings_count": len(findings)})

        if profile:
            existing_profile = db.query(DatasetProfile).filter_by(session_id=session_id).first()
            if existing_profile:
                profile.profile_id = existing_profile.profile_id
            db.merge(profile)

        db.query(ValidationFinding).filter(ValidationFinding.session_id == session_id).delete()
        for finding in findings:
            cast(Any, finding).session_id = session_id
            db.add(finding)

        commit_session_mutation(session, expected_version=expected_version)
        logger.reflect("Dataset profile and validation findings committed", extra={"session_id": session.session_id, "user_id": user_id, "findings_count": len(findings)})

        return session


# #endregion save_profile_and_findings


# #region save_recovery_state [C:4] [TYPE Function]
# @BRIEF Persist imported filters, template variables, and initial execution mappings for one owned session.
# @PRE  session_id belongs to user_id.
# @POST  Recovery state persisted to database.
# @SIDE_EFFECT  Writes to database.
def save_recovery_state(
    db: Session,
    get_owned_session,
    require_session_version,
    commit_session_mutation,
    load_session_detail_fn,
    session_id: str,
    user_id: str,
    imported_filters: list[ImportedFilter],
    template_variables: list[TemplateVariable],
    execution_mappings: list[ExecutionMapping],
    expected_version: int | None = None,
) -> DatasetReviewSession:
    with belief_scope("save_recovery_state"):
        session = get_owned_session(session_id, user_id)
        if expected_version is not None:
            require_session_version(session, expected_version)
        logger.reason("Persisting dataset review recovery bootstrap state", extra={"session_id": session_id, "user_id": user_id, "imported_filters_count": len(imported_filters), "template_variables_count": len(template_variables), "execution_mappings_count": len(execution_mappings)})

        db.query(ExecutionMapping).filter(ExecutionMapping.session_id == session_id).delete()
        db.query(TemplateVariable).filter(TemplateVariable.session_id == session_id).delete()
        db.query(ImportedFilter).filter(ImportedFilter.session_id == session_id).delete()

        for f in imported_filters:
            cast(Any, f).session_id = session_id
            db.add(f)
        for tv in template_variables:
            cast(Any, tv).session_id = session_id
            db.add(tv)
        db.flush()
        for em in execution_mappings:
            cast(Any, em).session_id = session_id
            db.add(em)

        commit_session_mutation(session, expected_version=expected_version)
        logger.reflect("Dataset review recovery bootstrap state committed", extra={"session_id": session.session_id, "user_id": user_id})
        return load_session_detail_fn(session_id, user_id)


# #endregion save_recovery_state


# #region save_preview [C:3] [TYPE Function]
# @BRIEF Persist a preview snapshot and mark prior session previews stale.
# @PRE  session_id belongs to user_id and preview is prepared for the same session aggregate.
# @POST  preview is persisted and the session points to the latest preview identifier.
# @SIDE_EFFECT  updates prior preview statuses, inserts a preview row, mutates the parent session, and commits.
def save_preview(
    db: Session,
    get_owned_session,
    require_session_version,
    commit_session_mutation,
    session_id: str,
    user_id: str,
    preview: CompiledPreview,
    expected_version: int | None = None,
) -> CompiledPreview:
    with belief_scope("save_preview"):
        session = get_owned_session(session_id, user_id)
        session_record = cast(Any, session)
        if expected_version is not None:
            require_session_version(session, expected_version)
        logger.reason("Persisting compiled preview and staling previous preview snapshots", extra={"session_id": session_id, "user_id": user_id})

        db.query(CompiledPreview).filter(CompiledPreview.session_id == session_id).update({"preview_status": "stale"})
        db.add(preview)
        db.flush()
        session_record.last_preview_id = preview.preview_id

        commit_session_mutation(session, refresh_targets=[preview], expected_version=expected_version)
        logger.reflect("Compiled preview committed as latest session preview", extra={"session_id": session.session_id, "preview_id": preview.preview_id})
        return preview


# #endregion save_preview


# #region save_run_context [C:3] [TYPE Function]
# @BRIEF Persist an immutable launch audit snapshot for an owned session.
# @PRE  session_id belongs to user_id and run_context targets the same aggregate.
# @POST  run context is persisted and linked as the latest launch snapshot for the session.
# @SIDE_EFFECT  inserts a run-context row, mutates the parent session pointer, and commits.
def save_run_context(
    db: Session,
    get_owned_session,
    require_session_version,
    commit_session_mutation,
    session_id: str,
    user_id: str,
    run_context: DatasetRunContext,
    expected_version: int | None = None,
) -> DatasetRunContext:
    with belief_scope("save_run_context"):
        session = get_owned_session(session_id, user_id)
        session_record = cast(Any, session)
        if expected_version is not None:
            require_session_version(session, expected_version)
        logger.reason("Persisting dataset run context audit snapshot", extra={"session_id": session_id, "user_id": user_id})

        db.add(run_context)
        db.flush()
        session_record.last_run_context_id = run_context.run_context_id

        commit_session_mutation(session, refresh_targets=[run_context], expected_version=expected_version)
        logger.reflect("Dataset run context committed as latest launch snapshot", extra={"session_id": session.session_id, "run_context_id": run_context.run_context_id})
        return run_context


# #endregion save_run_context


# #endregion SessionRepositoryMutations
