from collections.abc import Sequence

import dagster as dg
import pytest
from dagster import DagsterEventType
from dagster._annotations import get_deprecated_info, is_deprecated
from dagster._core.execution.execution_result import ExecutionResult


def expt_results_for_compute_step(
    result: ExecutionResult, node_name: str
) -> Sequence[dg.DagsterEvent]:
    return [
        compute_step_event
        for compute_step_event in result.events_for_node(node_name)
        if compute_step_event.event_type == DagsterEventType.STEP_EXPECTATION_RESULT
    ]


def test_successful_expectation_in_compute_step():
    @dg.op(out={})
    def success_expectation_op(_context):
        yield dg.ExpectationResult(success=True, description="This is always true.")

    job_def = dg.GraphDefinition(
        name="success_expectation_in_compute_pipeline",
        node_defs=[success_expectation_op],
    ).to_job()

    result = job_def.execute_in_process()

    assert result
    assert result.success

    expt_results = expt_results_for_compute_step(result, "success_expectation_op")

    assert len(expt_results) == 1
    expt_result = expt_results[0]
    assert expt_result.event_specific_data.expectation_result.success  # pyright: ignore[reportOptionalMemberAccess,reportAttributeAccessIssue]
    assert expt_result.event_specific_data.expectation_result.description == "This is always true."  # pyright: ignore[reportOptionalMemberAccess,reportAttributeAccessIssue]


def test_failed_expectation_in_compute_step():
    @dg.op(out={})
    def failure_expectation_op(_context):
        yield dg.ExpectationResult(success=False, description="This is always false.")

    job_def = dg.GraphDefinition(
        name="failure_expectation_in_compute_pipeline",
        node_defs=[failure_expectation_op],
    ).to_job()

    result = job_def.execute_in_process()

    assert result
    assert result.success
    expt_results = expt_results_for_compute_step(result, "failure_expectation_op")

    assert len(expt_results) == 1
    expt_result = expt_results[0]
    assert not expt_result.event_specific_data.expectation_result.success  # pyright: ignore[reportOptionalMemberAccess,reportAttributeAccessIssue]
    assert expt_result.event_specific_data.expectation_result.description == "This is always false."  # pyright: ignore[reportOptionalMemberAccess,reportAttributeAccessIssue]


def test_return_expectation_failure():
    @dg.op
    def return_expectation_failure(_context):
        return dg.ExpectationResult(success=True, description="This is always true.")

    job_def = dg.GraphDefinition(
        name="success_expectation_in_compute_pipeline",
        node_defs=[return_expectation_failure],
    ).to_job()

    with pytest.raises(
        dg.DagsterInvariantViolationError,
        match=(
            "If you are returning an AssetMaterialization or an ExpectationResult from op you must"
            " yield them directly"
        ),
    ):
        job_def.execute_in_process()


def test_expectation_result_deprecated() -> None:
    assert is_deprecated(dg.ExpectationResult)
    assert (
        get_deprecated_info(dg.ExpectationResult).additional_warn_text
        == "If using assets, use AssetCheckResult and @asset_check instead."
    )
