from flask import Flask

from services.pchome_mapping_backlog_service import (
    build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_rollback_evidence_package,
)


def _replay_package():
    return {
        "result": "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_RECEIPT_REPLAYED",
        "summary": {
            "target_selector_count": 2,
            "post_apply_readback_pass_count": 2,
            "executor_receipt_hash_match_count": 1,
            "missing_artifact_count": 0,
            "writes_database_count": 0,
        },
        "missing_artifacts": [],
        "receipt_replay": {"run_id": "run-test"},
    }


def _drift_package(drift_count=0):
    drift_items = []
    if drift_count:
        drift_items = [
            {
                "selector_id": "receipt-1",
                "momo_icode": "MOMO-1",
                "expected_pchome_id": "PCH-EXPECTED",
                "actual_pchome_id": "PCH-DRIFT",
                "expected_momo_name": "Expected product",
                "actual_momo_name": "Expected product",
            }
        ]
    return {
        "result": (
            "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_DETECTED"
            if drift_count
            else "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_VERIFIED"
        ),
        "summary": {
            "target_selector_count": 2,
            "post_apply_readback_pass_count": 2 - drift_count,
            "drift_count": drift_count,
            "drift_verified_count": 0 if drift_count else 1,
            "drift_verifier_artifact_hash_match_count": 1,
            "writes_database_count": 0,
        },
        "drift_items": drift_items,
    }


def _recovery_package(drift_count=0):
    actions = []
    if drift_count:
        actions = [
            {
                "action_id": "rollback-action-1",
                "selector_id": "receipt-1",
                "momo_icode": "MOMO-1",
                "expected_pchome_id": "PCH-EXPECTED",
                "actual_pchome_id": "PCH-DRIFT",
                "status": "ready_for_controlled_reapply",
                "rollback_sql_shape": "UPDATE pchome_product_matches SET pchome_id = :actual_pchome_id",
                "controlled_reapply_sql_shape": "UPDATE pchome_product_matches SET pchome_id = :expected_pchome_id",
                "selector_bindings": {
                    "momo_icode": "MOMO-1",
                    "expected_pchome_id": "PCH-EXPECTED",
                    "actual_pchome_id": "PCH-DRIFT",
                },
                "acceptance_gates": ["post_reapply_drift_verifier_returns_zero_drift"],
                "writes_database": False,
            }
        ]
    return {
        "result": (
            "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_RECOVERY_PACKAGE_READY"
            if drift_count
            else "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_RECOVERY_NOT_REQUIRED"
        ),
        "summary": {
            "drift_count": drift_count,
            "drift_recovery_action_count": len(actions),
            "drift_reapply_ready_count": len(actions),
            "recovery_artifact_hash_match_count": 1,
            "writes_database_count": 0,
        },
        "recovery_actions": actions,
        "rollback_plan": {
            "rollback_action_count": len(actions),
            "executes_in_package": False,
            "writes_database": False,
        },
    }


def _compact_package(drift_count=0):
    return {
        "result": (
            "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_COMPACT_READBACK_DRIFT_REQUIRES_RECOVERY"
            if drift_count
            else "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_COMPACT_READBACK_VERIFIED"
        ),
        "summary": {
            "target_selector_count": 2,
            "post_apply_readback_pass_count": 2 - drift_count,
            "drift_count": drift_count,
            "drift_recovery_action_count": drift_count,
            "compact_readback_artifact_hash_match_count": 1,
            "writes_database_count": 0,
        },
        "compact_readback": {
            "status": "blocked" if drift_count else "completed",
            "next_machine_action": (
                "run_controlled_reapply_check_mode"
                if drift_count
                else "keep_monitoring_drift"
            ),
        },
    }


def _retention_package():
    return {
        "result": "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_ARTIFACT_RETENTION_POLICY_READY",
        "success": True,
        "summary": {
            "artifact_count": 8,
            "protected_active_chain_count": 4,
            "retention_artifact_hash_match_count": 1,
            "writes_database_count": 0,
        },
    }


def test_rollback_evidence_no_drift_builds_noop_machine_evidence():
    package = build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_rollback_evidence_package(
        source_receipt_replay=_replay_package(),
        source_drift_verifier=_drift_package(),
        source_drift_recovery=_recovery_package(),
        source_compact_readback=_compact_package(),
        source_artifact_retention=_retention_package(),
    )

    assert package["policy"] == (
        "read_only_pchome_growth_direct_mapping_retry_candidate_exception_controlled_apply_rollback_evidence"
    )
    assert package["result"] == (
        "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_ROLLBACK_EVIDENCE_NOT_REQUIRED"
    )
    assert package["summary"]["controlled_apply_family_count"] == 5
    assert package["summary"]["drift_count"] == 0
    assert package["summary"]["rollback_required_count"] == 0
    assert package["summary"]["rollback_evidence_ready_count"] == 1
    assert package["summary"]["primary_human_gate_count"] == 0
    assert package["rollback_evidence"]["status"] == "rollback_not_required"
    assert package["rollback_actions"] == []
    assert package["safety"]["executes_sql"] is False
    assert package["safety"]["writes_database"] is False
    assert package["all_checks_passed"] is True


def test_rollback_evidence_drift_ready_outputs_actions_without_db_write():
    package = build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_rollback_evidence_package(
        source_receipt_replay=_replay_package(),
        source_drift_verifier=_drift_package(drift_count=1),
        source_drift_recovery=_recovery_package(drift_count=1),
        source_compact_readback=_compact_package(drift_count=1),
        source_artifact_retention=_retention_package(),
    )

    assert package["result"] == "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_ROLLBACK_EVIDENCE_READY"
    assert package["summary"]["drift_count"] == 1
    assert package["summary"]["rollback_required_count"] == 1
    assert package["summary"]["rollback_action_count"] == 1
    assert package["summary"]["rollback_ready_action_count"] == 1
    assert package["rollback_evidence"]["status"] == "rollback_ready"
    assert package["rollback_evidence"]["next_machine_action"] == "run_controlled_reapply_check_mode"
    assert package["rollback_actions"][0]["momo_icode"] == "MOMO-1"
    assert "UPDATE pchome_product_matches" in package["rollback_actions"][0]["rollback_sql_shape"]
    assert package["rollback_actions"][0]["executes_in_package"] is False
    assert package["rollback_actions"][0]["writes_database"] is False
    assert package["safety"]["executes_rollback"] is False
    assert package["safety"]["executes_reapply"] is False
    assert package["safety"]["writes_database"] is False
    assert package["all_checks_passed"] is True


def test_rollback_evidence_route_uses_engine_and_returns_source_endpoint(monkeypatch):
    from routes import ai_routes as routes

    class FakeEngine:
        disposed = False

        def dispose(self):
            self.disposed = True

    fake_engine = FakeEngine()
    monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", lambda _path: fake_engine)

    def fake_builder(**kwargs):
        assert kwargs["engine"] is fake_engine
        return {
            "success": True,
            "policy": "read_only_pchome_growth_direct_mapping_retry_candidate_exception_controlled_apply_rollback_evidence",
            "result": "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_ROLLBACK_EVIDENCE_NOT_REQUIRED",
            "summary": {"writes_database_count": 0, "primary_human_gate_count": 0},
            "safety": {"writes_database": False},
        }

    monkeypatch.setattr(
        "services.pchome_mapping_backlog_service.build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_rollback_evidence_package",
        fake_builder,
    )

    app = Flask(__name__)
    with app.test_request_context(
        "/api/ai/pchome-growth/mapping-backlog/direct-mapping-retry-candidate-exception-controlled-apply-rollback-evidence-package"
    ):
        response = (
            routes
            .api_pchome_growth_direct_mapping_retry_candidate_exception_controlled_apply_rollback_evidence_package
            .__wrapped__()
        )

    payload = response.get_json()
    assert fake_engine.disposed is True
    assert payload["source_endpoint"] == (
        "/api/ai/pchome-growth/mapping-backlog/direct-mapping-retry-candidate-exception-controlled-apply-artifact-retention-package"
    )
    assert payload["summary"]["primary_human_gate_count"] == 0
    assert payload["safety"]["writes_database"] is False
