# #region AssistantDatasetReviewDispatch [C:4] [TYPE Module] [SEMANTICS assistant, dataset, review, dispatch, confirm]
# @BRIEF Dispatch and confirmation handling for dataset-review assistant intents.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantDatasetReview]
# @RELATION DEPENDS_ON -> [DatasetReviewOrchestrator]
# @RELATION DEPENDS_ON -> [AssistantSchemas]
# @INVARIANT  Dataset review dispatch requires valid session version for write operations.

from __future__ import annotations

from datetime import UTC, datetime
from typing import Any

from fastapi import HTTPException, status
from sqlalchemy.orm import Session

from src.api.routes.dataset_review import FieldSemanticUpdateRequest, _update_semantic_field_state
from src.core.config_manager import ConfigManager
from src.core.logger import belief_scope, logger
from src.models.dataset_review import (
    ApprovalState,
    ReadinessState,
    RecommendedAction,
)
from src.schemas.auth import User
from src.services.dataset_review.orchestrator import (
    DatasetReviewOrchestrator,
    PreparePreviewCommand,
)
from src.services.dataset_review.repositories.session_repository import (
    DatasetReviewSessionRepository,
    DatasetReviewSessionVersionConflictError,
)

from ._schemas import (
    AssistantAction,
)


# #region _dataset_review_conflict_http_exception [C:2] [TYPE Function]
# @BRIEF Convert dataset-review optimistic-lock conflicts into shared 409 assistant semantics.
def _dataset_review_conflict_http_exception(
    exc: DatasetReviewSessionVersionConflictError,
) -> HTTPException:
    return HTTPException(
        status_code=status.HTTP_409_CONFLICT,
        detail={
            "error_code": "session_version_conflict",
            "message": str(exc),
            "session_id": exc.session_id,
            "expected_version": exc.expected_version,
            "actual_version": exc.actual_version,
        },
    )


# #endregion _dataset_review_conflict_http_exception


# #region _dispatch_dataset_review_intent [C:4] [TYPE Function]
# @BRIEF Route confirmed dataset-review assistant intents through existing backend dataset-review APIs and orchestration boundaries.
# @RELATION CALLS -> DatasetReviewOrchestrator
# @PRE  context contains valid session data and user intent.
# @POST  Returns a structured response with planned actions and confirmations.
# @SIDE_EFFECT  May update session state and enqueue tasks.
async def _dispatch_dataset_review_intent(
    intent: dict[str, Any],
    current_user: User,
    config_manager: ConfigManager,
    db: Session,
) -> tuple[str, str | None, list[AssistantAction]]:
    with belief_scope("_dispatch_dataset_review_intent"):
        logger.reason(
            "Dispatching assistant dataset-review intent",
            extra={"operation": intent.get("operation")},
        )
        entities = intent.get("entities", {})
        session_id = entities.get("dataset_review_session_id")
        session_version = entities.get("session_version")
        if not session_id or session_version is None:
            raise HTTPException(
                status_code=422,
                detail="Missing dataset_review_session_id/session_version",
            )

        operation = str(intent.get("operation") or "")
        repository = DatasetReviewSessionRepository(db)
        if operation == "dataset_review_answer_context":
            summary = str(entities.get("summary") or "")
            logger.reflect(
                "Returned assistant-safe dataset review context summary",
                extra={"session_id": session_id, "operation": operation},
            )
            return summary, None, []

        session = repository.load_session_detail(session_id, current_user.id)
        if session is None or session.user_id != current_user.id:
            logger.explore(
                "Assistant dataset-review intent rejected because session was not found",
                extra={"session_id": session_id, "user_id": current_user.id},
            )
            raise HTTPException(
                status_code=404, detail="Dataset review session not found"
            )

        try:
            repository.require_session_version(session, int(session_version))
        except DatasetReviewSessionVersionConflictError as exc:
            logger.explore(
                "Assistant dataset-review intent rejected due to stale session version",
                extra={
                    "session_id": exc.session_id,
                    "expected_version": exc.expected_version,
                    "actual_version": exc.actual_version,
                    "operation": operation,
                },
            )
            raise _dataset_review_conflict_http_exception(exc) from exc

        logger.reason(
            "Dispatching confirmed assistant dataset-review intent",
            extra={
                "session_id": session_id,
                "session_version": session_version,
                "operation": operation,
            },
        )

        if operation == "dataset_review_approve_mappings":
            mapping_ids = list(dict.fromkeys(entities.get("mapping_ids") or []))
            if not mapping_ids:
                raise HTTPException(
                    status_code=409, detail="No pending mappings to approve"
                )
            updated_count = 0
            for mapping in session.execution_mappings:
                if mapping.mapping_id not in mapping_ids:
                    continue
                mapping.approval_state = ApprovalState.APPROVED
                mapping.approved_by_user_id = current_user.id
                mapping.approved_at = datetime.now(UTC)
                updated_count += 1
            if updated_count == 0:
                raise HTTPException(
                    status_code=409, detail="No matching mappings available to approve"
                )
            session.last_activity_at = datetime.now(UTC)
            if session.readiness_state == ReadinessState.MAPPING_REVIEW_NEEDED:
                session.recommended_action = RecommendedAction.GENERATE_SQL_PREVIEW
            repository.bump_session_version(session)
            repository.db.commit()
            repository.db.refresh(session)
            repository.event_logger.log_for_session(
                session,
                actor_user_id=current_user.id,
                event_type="assistant_mapping_approval",
                event_summary="Assistant-approved warning-sensitive mappings persisted",
                event_details={
                    "mapping_ids": mapping_ids,
                    "count": updated_count,
                    "version": int(getattr(session, "version", 0) or 0),
                },
            )
            logger.reflect(
                "Assistant mapping approval persisted within optimistic-lock boundary",
                extra={
                    "session_id": session_id,
                    "updated_count": updated_count,
                    "version": int(getattr(session, "version", 0) or 0),
                },
            )
            return (
                f"Approved {updated_count} mapping(s) for dataset review session {session_id}.",
                None,
                [
                    AssistantAction(
                        type="focus_target",
                        label="Open mapping review",
                        target="mapping",
                    )
                ],
            )

        if operation == "dataset_review_set_field_semantics":
            field_id = str(entities.get("field_id") or "").strip()
            if not field_id:
                raise HTTPException(status_code=422, detail="Missing field_id")
            field = next(
                (item for item in session.semantic_fields if item.field_id == field_id),
                None,
            )
            if field is None:
                raise HTTPException(status_code=404, detail="Semantic field not found")
            update_request = FieldSemanticUpdateRequest(
                candidate_id=entities.get("candidate_id"),
                verbose_name=entities.get("verbose_name"),
                description=entities.get("description"),
                display_format=entities.get("display_format"),
                lock_field=bool(entities.get("lock_field", False)),
            )
            try:
                _update_semantic_field_state(
                    field, update_request, changed_by="assistant"
                )
            except HTTPException:
                raise
            except ValueError as exc:
                raise HTTPException(status_code=400, detail=str(exc)) from exc
            session.last_activity_at = datetime.now(UTC)
            repository.bump_session_version(session)
            repository.db.commit()
            repository.db.refresh(session)
            repository.db.refresh(field)
            repository.event_logger.log_for_session(
                session,
                actor_user_id=current_user.id,
                event_type="assistant_field_semantics_updated",
                event_summary="Assistant semantic field update persisted",
                event_details={
                    "field_id": field.field_id,
                    "candidate_id": entities.get("candidate_id"),
                    "lock_field": bool(entities.get("lock_field", False)),
                    "version": int(getattr(session, "version", 0) or 0),
                },
            )
            logger.reflect(
                "Assistant semantic field update committed safely",
                extra={
                    "session_id": session_id,
                    "field_id": field_id,
                    "version": int(getattr(session, "version", 0) or 0),
                },
            )
            return (
                f"Updated semantic field {field.field_name} for dataset review session {session_id}.",
                None,
                [
                    AssistantAction(
                        type="focus_target",
                        label="Open semantic review",
                        target=f"field:{field.field_id}",
                    )
                ],
            )

        if operation == "dataset_review_generate_sql_preview":
            orchestrator = DatasetReviewOrchestrator(
                repository=repository,
                config_manager=config_manager,
            )
            result = orchestrator.prepare_launch_preview(
                PreparePreviewCommand(
                    user=current_user,
                    session_id=session_id,
                    expected_version=int(session_version),
                )
            )
            preview_status = getattr(
                result.preview.preview_status, "value", result.preview.preview_status
            )
            logger.reflect(
                "Assistant-triggered Superset preview generation completed",
                extra={
                    "session_id": session_id,
                    "preview_status": preview_status,
                },
            )
            return (
                f"SQL preview {preview_status} for dataset review session {session_id}.",
                None,
                [
                    AssistantAction(
                        type="focus_target",
                        label="Open SQL preview",
                        target="sql-preview",
                    )
                ],
            )

        raise HTTPException(
            status_code=400, detail="Unsupported dataset review operation"
        )


# #endregion _dispatch_dataset_review_intent


# #endregion AssistantDatasetReviewDispatch
