# #region AssistantDatasetReview [C:4] [TYPE Module] [SEMANTICS assistant, dataset, review, context, intent]
# @BRIEF Dataset review context loading and intent planning for the assistant API.
# @LAYER API
# @RELATION DEPENDS_ON -> [DatasetReviewOrchestrator]
# @RELATION DEPENDS_ON -> [AssistantSchemas]
# @RELATION DISPATCHES -> [AssistantDatasetReviewDispatch]
# @INVARIANT  Dataset review operations are always scoped to the owner's session.

from __future__ import annotations

import re
from typing import Any

from fastapi import HTTPException
from sqlalchemy.orm import Session

from src.core.logger import belief_scope, logger
from src.core.utils.superset_context_extractor import (
    sanitize_imported_filter_for_assistant,
)
from src.models.dataset_review import (
    ApprovalState,
    DatasetReviewSession,
)
from src.schemas.auth import User
from src.services.dataset_review.repositories.session_repository import (
    DatasetReviewSessionRepository,
)


# #region _serialize_dataset_review_context [C:4] [TYPE Function]
# @BRIEF Build assistant-safe dataset-review context snapshot with masked imported-filter payloads for session-scoped assistant routing.
# @RELATION DEPENDS_ON -> [DatasetReviewSession]
# @PRE  session_id is a valid active review session identifier.
# @POST  Returns a serializable dictionary containing the complete review context.
# @SIDE_EFFECT  Reads session data from the database.
def _serialize_dataset_review_context(session: DatasetReviewSession) -> dict[str, Any]:
    with belief_scope('_serialize_dataset_review_context'):
        logger.reason('Belief protocol reasoning checkpoint for _serialize_dataset_review_context')
        latest_preview = None
        previews = getattr(session, 'previews', []) or []
        if previews:
            latest_preview = previews[-1]
        logger.reflect('Belief protocol postcondition checkpoint for _serialize_dataset_review_context')
        return {'session_id': session.session_id, 'version': int(getattr(session, 'version', 0) or 0), 'dataset_ref': session.dataset_ref, 'dataset_id': session.dataset_id, 'environment_id': session.environment_id, 'readiness_state': session.readiness_state.value, 'recommended_action': session.recommended_action.value, 'status': session.status.value, 'current_phase': session.current_phase.value, 'findings': [{'finding_id': item.finding_id, 'code': item.code, 'severity': item.severity.value, 'message': item.message, 'resolution_state': item.resolution_state.value} for item in getattr(session, 'findings', [])], 'imported_filters': [sanitize_imported_filter_for_assistant({'filter_id': item.filter_id, 'filter_name': item.filter_name, 'display_name': item.display_name, 'raw_value': item.raw_value, 'raw_value_masked': bool(getattr(item, 'raw_value_masked', False)), 'normalized_value': item.normalized_value, 'source': getattr(item.source, 'value', item.source), 'confidence_state': getattr(item.confidence_state, 'value', item.confidence_state), 'requires_confirmation': bool(item.requires_confirmation), 'recovery_status': getattr(item.recovery_status, 'value', item.recovery_status), 'notes': item.notes}) for item in getattr(session, 'imported_filters', [])], 'mappings': [{'mapping_id': item.mapping_id, 'filter_id': item.filter_id, 'variable_id': item.variable_id, 'mapping_method': getattr(item.mapping_method, 'value', item.mapping_method), 'effective_value': item.effective_value, 'approval_state': getattr(item.approval_state, 'value', item.approval_state), 'requires_explicit_approval': bool(item.requires_explicit_approval)} for item in getattr(session, 'execution_mappings', [])], 'semantic_fields': [{'field_id': item.field_id, 'field_name': item.field_name, 'verbose_name': item.verbose_name, 'description': item.description, 'display_format': item.display_format, 'provenance': getattr(item.provenance, 'value', item.provenance), 'is_locked': bool(item.is_locked), 'needs_review': bool(getattr(item, 'needs_review', False)), 'candidates': [{'candidate_id': c.candidate_id, 'field_id': c.field_id, 'verbose_name': c.proposed_verbose_name, 'description': c.proposed_description, 'display_format': c.proposed_display_format, 'status': getattr(c.status, 'value', c.status), 'source': c.source_id, 'score': c.confidence_score, 'created_at': c.created_at.isoformat() if c.created_at else None} for c in getattr(item, 'candidates', [])]} for item in getattr(session, 'semantic_fields', [])]}


# #endregion _serialize_dataset_review_context


# #region _load_dataset_review_context [C:4] [TYPE Function]
# @BRIEF Load owner-scoped dataset-review context for assistant planning and grounded response generation.
# @RELATION DEPENDS_ON -> [DatasetReviewSessionRepository]
# @PRE  session_id is a valid active review session identifier.
# @POST  Returns a loaded context object with session data and findings.
# @SIDE_EFFECT  Reads session data from the database.
def _load_dataset_review_context(dataset_review_session_id: str | None, current_user: User, db: Session) -> dict[str, Any] | None:
    with belief_scope('_load_dataset_review_context'):
        if not dataset_review_session_id:
            return None
        logger.reason('Belief protocol reasoning checkpoint for _load_dataset_review_context')
        repository = DatasetReviewSessionRepository(db)
        session = repository.load_session_detail(dataset_review_session_id, current_user.id)
        if session is None or session.user_id != current_user.id:
            raise HTTPException(status_code=404, detail='Dataset review session not found')
        logger.reflect('Belief protocol postcondition checkpoint for _load_dataset_review_context')
        return _serialize_dataset_review_context(session)


# #endregion _load_dataset_review_context


# #region _extract_dataset_review_target [C:2] [TYPE Function]
# @BRIEF Extract structured dataset-review focus target hints embedded in assistant prompts.
def _extract_dataset_review_target(message: str) -> tuple[str | None, str | None]:
    match = re.search(
        r"(?:target|focus)\s*[:=]\s*(field|mapping|finding|filter)[:=]([A-Za-z0-9._-]+)",
        str(message or ""),
        re.IGNORECASE,
    )
    if not match:
        return None, None
    return match.group(1).lower(), match.group(2)


# #endregion _extract_dataset_review_target


# #region _match_dataset_review_field [C:2] [TYPE Function]
# @BRIEF Resolve one semantic field from assistant-visible context by id or user-visible label.
def _match_dataset_review_field(
    dataset_context: dict[str, Any],
    message: str,
) -> dict[str, Any] | None:
    target_kind, target_id = _extract_dataset_review_target(message)
    fields = dataset_context.get("semantic_fields", []) or []
    if target_kind == "field" and target_id:
        return next(
            (item for item in fields if str(item.get("field_id")) == str(target_id)),
            None,
        )

    normalized_message = str(message or "").lower()
    for field in fields:
        if str(field.get("field_id", "")).lower() in normalized_message:
            return field
        field_name = str(field.get("field_name", "")).lower()
        if field_name and field_name in normalized_message:
            return field
        verbose_name = str(field.get("verbose_name", "")).lower()
        if verbose_name and verbose_name in normalized_message:
            return field
    return None


# #endregion _match_dataset_review_field


# #region _extract_quoted_segment [C:2] [TYPE Function]
# @BRIEF Extract one quoted assistant command segment after a label token.
def _extract_quoted_segment(message: str, label: str) -> str | None:
    pattern = rf"{label}\s*[=:]?\s*[\"']([^\"']+)[\"']"
    match = re.search(pattern, str(message or ""), re.IGNORECASE)
    return match.group(1).strip() if match else None


# #endregion _extract_quoted_segment


# #region _plan_dataset_review_intent [C:3] [TYPE Function]
# @BRIEF Parse session-scoped dataset-review assistant commands before falling back to generic assistant tool routing.
# @RELATION CALLS -> DatasetReviewOrchestrator
def _plan_dataset_review_intent(
    message: str,
    dataset_context: dict[str, Any],
) -> dict[str, Any] | None:
    lower = message.strip().lower()
    session_id = dataset_context["session_id"]
    session_version = int(dataset_context.get("version", 0) or 0)
    target_kind, target_id = _extract_dataset_review_target(message)

    if any(
        token in lower
        for token in [
            "approve mappings",
            "approve mapping",
            "подтверди мапп",
            "одобри мапп",
        ]
    ):
        pending_mapping_ids = [
            item["mapping_id"]
            for item in dataset_context.get("mappings", [])
            if item.get("requires_explicit_approval")
            and item.get("approval_state") != ApprovalState.APPROVED.value
        ]
        return {
            "domain": "dataset_review",
            "operation": "dataset_review_approve_mappings",
            "entities": {
                "dataset_review_session_id": session_id,
                "session_version": session_version,
                "mapping_ids": pending_mapping_ids,
            },
            "confidence": 0.95,
            "risk_level": "guarded",
            "requires_confirmation": True,
        }

    if any(
        token in lower
        for token in [
            "generate sql preview",
            "generate preview",
            "сгенерируй превью",
            "собери превью",
        ]
    ):
        return {
            "domain": "dataset_review",
            "operation": "dataset_review_generate_sql_preview",
            "entities": {
                "dataset_review_session_id": session_id,
                "session_version": session_version,
            },
            "confidence": 0.94,
            "risk_level": "guarded",
            "requires_confirmation": True,
        }

    if any(
        token in lower
        for token in [
            "set field semantics",
            "apply field semantics",
            "semantic override",
            "update semantic field",
            "установи семантик",
            "обнови семантик",
        ]
    ):
        field = _match_dataset_review_field(dataset_context, message)
        if field is None:
            return None
        candidate_id = None
        if any(
            token in lower for token in ["accept candidate", "apply candidate", "прими кандидат"]
        ):
            candidates = field.get("candidates") or []
            if candidates:
                candidate_id = candidates[0].get("candidate_id")
        verbose_name = _extract_quoted_segment(
            message, "verbose_name|verbose name|label"
        )
        description = _extract_quoted_segment(message, "description|desc")
        display_format = _extract_quoted_segment(
            message, "display_format|display format|format"
        )
        lock_field = any(
            token in lower for token in [" lock", "lock it", "зафикс", "закреп"]
        )
        if not any([candidate_id, verbose_name, description, display_format]):
            return None
        return {
            "domain": "dataset_review",
            "operation": "dataset_review_set_field_semantics",
            "entities": {
                "dataset_review_session_id": session_id,
                "session_version": session_version,
                "field_id": field.get("field_id") or target_id,
                "candidate_id": candidate_id,
                "verbose_name": verbose_name,
                "description": description,
                "display_format": display_format,
                "lock_field": lock_field,
            },
            "confidence": 0.9,
            "risk_level": "guarded",
            "requires_confirmation": True,
        }

    if any(
        token in lower
        for token in [
            "filters",
            "фильтр",
            "mapping",
            "маппинг",
            "preview",
            "превью",
            "finding",
            "ошиб",
        ]
    ):
        findings_count = len(dataset_context.get("findings", []))
        mappings_count = len(dataset_context.get("mappings", []))
        filters_count = len(dataset_context.get("imported_filters", []))
        preview = dataset_context.get("preview") or {}
        preview_status = preview.get("preview_status") or "missing"
        masked_filters = dataset_context.get("imported_filters", [])
        return {
            "domain": "dataset_review",
            "operation": "dataset_review_answer_context",
            "entities": {
                "dataset_review_session_id": session_id,
                "summary": (
                    f"Session {session_id}: readiness={dataset_context['readiness_state']}, "
                    f"recommended_action={dataset_context['recommended_action']}, "
                    f"filters={filters_count}, mappings={mappings_count}, findings={findings_count}, "
                    f"preview_status={preview_status}, imported_filters={masked_filters}"
                ),
            },
            "confidence": 0.8,
            "risk_level": "safe",
            "requires_confirmation": False,
        }
    return None


# #endregion _plan_dataset_review_intent


# #endregion AssistantDatasetReview
