# #region Test.DatasetReviewMappingModels [C:3] [TYPE Module] [SEMANTICS test,sqlalchemy,dataset,review,mapping,execution,model]
# @BRIEF Verify ExecutionMapping SQLAlchemy model — construction, defaults, JSON columns, approval gate, FK constraints.
# @RELATION BINDS_TO -> [DatasetReviewMappingModels]
# @TEST_EDGE: approval_gate -> requires_explicit_approval defaults False
# @TEST_EDGE: approval_state_default -> Defaults to NOT_REQUIRED
# @TEST_EDGE: effective_value_none -> effective_value can be None
# @TEST_EDGE: warning_level_default -> warning_level can be None
import uuid
from datetime import UTC, datetime

from sqlalchemy import create_engine, text
from sqlalchemy.orm import Session as SASession
import pytest

from src.models.dataset_review_pkg._session_models import DatasetReviewSession
from src.models.dataset_review_pkg._mapping_models import (
    Base,
    ExecutionMapping,
)
from src.models.dataset_review_pkg._enums import (
    ApprovalState,
    MappingMethod,
    MappingWarningLevel,
)
from src.models.mapping import Environment


@pytest.fixture(scope="function")
def db_session():
    """Fresh in-memory SQLite with FK enforcement using single connection."""
    engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
    conn = engine.connect()
    conn.execute(text("PRAGMA foreign_keys=ON"))

    # Create the raw users table FIRST — multiple model tables FK-reference it
    conn.execute(text("CREATE TABLE IF NOT EXISTS users (id VARCHAR PRIMARY KEY)"))

    from src.models.mapping import Base as MappingBase
    MappingBase.metadata.create_all(conn)
    Base.metadata.create_all(conn)
    conn.commit()

    session = SASession(bind=conn)
    env = Environment(id="env-1", name="Test", url="https://example.com", credentials_id="cred-1")
    session.add(env)
    session.execute(text("INSERT OR IGNORE INTO users (id) VALUES ('user-1')"))
    session.commit()
    yield session
    session.close()
    conn.close()


def _create_session(db_session) -> DatasetReviewSession:
    rev = DatasetReviewSession(
        user_id="user-1", environment_id="env-1",
        source_kind="superset", source_input="SELECT 1", dataset_ref="ds_1",
    )
    db_session.add(rev)
    db_session.flush()
    return rev


class TestExecutionMapping:
    """Verify ExecutionMapping model."""

    def test_create_minimal(self, db_session):
        """Minimal required fields produce valid mapping with defaults."""
        session = _create_session(db_session)
        mapping = ExecutionMapping(
            session_id=session.session_id,
            filter_id="filter-1",
            variable_id="var-1",
            mapping_method=MappingMethod.DIRECT_MATCH,
            raw_input_value={"values": ["active"]},
        )
        db_session.add(mapping)
        db_session.flush()

        assert mapping.mapping_id is not None
        assert isinstance(uuid.UUID(mapping.mapping_id), uuid.UUID)
        assert mapping.session_id == session.session_id
        assert mapping.filter_id == "filter-1"
        assert mapping.variable_id == "var-1"
        assert mapping.mapping_method == MappingMethod.DIRECT_MATCH
        assert mapping.raw_input_value == {"values": ["active"]}

        # Defaults
        assert mapping.effective_value is None
        assert mapping.transformation_note is None
        assert mapping.warning_level is None
        assert mapping.requires_explicit_approval is False
        assert mapping.approval_state == ApprovalState.NOT_REQUIRED
        assert mapping.approved_by_user_id is None
        assert mapping.approved_at is None
        assert mapping.created_at is not None
        assert mapping.updated_at is not None

    def test_all_fields(self, db_session):
        """All fields populated."""
        session = _create_session(db_session)
        approved_at = datetime.now(UTC)
        mapping = ExecutionMapping(
            session_id=session.session_id,
            filter_id="f-1",
            variable_id="v-1",
            mapping_method=MappingMethod.HEURISTIC_MATCH,
            raw_input_value={"values": ["x", "y"]},
            effective_value={"values": ["X", "Y"]},
            transformation_note="Uppercased values",
            warning_level=MappingWarningLevel.LOW,
            requires_explicit_approval=True,
            approval_state=ApprovalState.PENDING,
            approved_by_user_id="user-2",
            approved_at=approved_at,
        )
        db_session.add(mapping)
        db_session.flush()

        assert mapping.effective_value == {"values": ["X", "Y"]}
        assert mapping.transformation_note == "Uppercased values"
        assert mapping.warning_level == MappingWarningLevel.LOW
        assert mapping.requires_explicit_approval is True
        assert mapping.approval_state == ApprovalState.PENDING
        assert mapping.approved_by_user_id == "user-2"
        assert mapping.approved_at == approved_at

    def test_effective_value_as_list(self, db_session):
        """Effective value can be a JSON list."""
        session = _create_session(db_session)
        mapping = ExecutionMapping(
            session_id=session.session_id, filter_id="f",
            variable_id="v", mapping_method=MappingMethod.SEMANTIC_MATCH,
            raw_input_value={}, effective_value=["a", "b", "c"],
        )
        db_session.add(mapping)
        db_session.flush()
        loaded = db_session.query(ExecutionMapping).filter_by(mapping_id=mapping.mapping_id).first()
        assert loaded.effective_value == ["a", "b", "c"]

    def test_all_mapping_methods(self, db_session):
        """All MappingMethod values accepted."""
        session = _create_session(db_session)
        for mm in MappingMethod:
            m = ExecutionMapping(
                session_id=session.session_id,
                filter_id=f"f_{mm.value}", variable_id="v",
                mapping_method=mm, raw_input_value={},
            )
            db_session.add(m)
        db_session.flush()
        assert db_session.query(ExecutionMapping).count() == len(MappingMethod)

    def test_all_approval_states(self, db_session):
        """All ApprovalState values accepted."""
        session = _create_session(db_session)
        for as_ in ApprovalState:
            m = ExecutionMapping(
                session_id=session.session_id, filter_id="f",
                variable_id="v", mapping_method=MappingMethod.MANUAL_OVERRIDE,
                raw_input_value={}, approval_state=as_,
                requires_explicit_approval=True if as_ in (ApprovalState.PENDING, ApprovalState.APPROVED) else False,
            )
            db_session.add(m)
        db_session.flush()
        assert db_session.query(ExecutionMapping).count() == len(ApprovalState)

    def test_cascade_delete(self, db_session):
        """Session delete cascades to execution mappings."""
        session = _create_session(db_session)
        m = ExecutionMapping(
            session_id=session.session_id, filter_id="f",
            variable_id="v", mapping_method=MappingMethod.DIRECT_MATCH,
            raw_input_value={},
        )
        db_session.add(m)
        db_session.flush()
        db_session.delete(session)
        db_session.flush()
        assert db_session.query(ExecutionMapping).count() == 0

    def test_relationship(self, db_session):
        """Navigate mapping -> session."""
        session = _create_session(db_session)
        m = ExecutionMapping(
            session_id=session.session_id, filter_id="f",
            variable_id="v", mapping_method=MappingMethod.DIRECT_MATCH,
            raw_input_value={},
        )
        db_session.add(m)
        db_session.flush()
        assert m.session is session

    def test_transformation_none_and_empty(self, db_session):
        """transformation_note accepts None or string."""
        session = _create_session(db_session)
        m1 = ExecutionMapping(
            session_id=session.session_id, filter_id="f1",
            variable_id="v1", mapping_method=MappingMethod.DIRECT_MATCH,
            raw_input_value={}, transformation_note="Note",
        )
        m2 = ExecutionMapping(
            session_id=session.session_id, filter_id="f2",
            variable_id="v2", mapping_method=MappingMethod.DIRECT_MATCH,
            raw_input_value={}, transformation_note=None,
        )
        db_session.add_all([m1, m2])
        db_session.flush()
        assert m1.transformation_note == "Note"
        assert m2.transformation_note is None
# #endregion Test.DatasetReviewMappingModels
