from __future__ import annotations

from pathlib import Path

from cf_ontology.pipeline_event_gatekeeper import (
    build_event_dedupe_key,
    emit_pipeline_event,
)
from cf_ontology.pipeline_states_store import PipelineStateStore


def _patch_gatekeeper_ontology_manager(monkeypatch, semantics_dir: Path) -> None:
    class _FakeOntologyManager:
        def __init__(self, *, load_resources: bool = True) -> None:
            self._semantics_dir = semantics_dir
            self.pipeline_states_db_path = semantics_dir / "cf-pipeline-states.duckdb"

    monkeypatch.setattr(
        "cf_ontology.pipeline_event_gatekeeper.OntologyManager",
        _FakeOntologyManager,
    )


def test_build_event_dedupe_key_is_stable_for_equivalent_payload() -> None:
    key_a = build_event_dedupe_key(
        pipeline_id="pipe-a",
        event_type="opcua_signal",
        source="manual",
        payload={"a": 1, "b": [2, 3]},
        source_event_id="src-1",
    )
    key_b = build_event_dedupe_key(
        pipeline_id="pipe-a",
        event_type="opcua_signal",
        source="manual",
        payload={"b": [2, 3], "a": 1},
        source_event_id="src-1",
    )
    assert key_a == key_b


def test_emit_pipeline_event_is_idempotent_with_same_dedupe(monkeypatch, tmp_path: Path) -> None:
    semantics_dir = tmp_path / "semantics"
    semantics_dir.mkdir(parents=True, exist_ok=True)
    _patch_gatekeeper_ontology_manager(monkeypatch, semantics_dir)

    result_a = emit_pipeline_event(
        pipeline_id="pipe-a",
        event_type="opcua_signal",
        source="manual",
        payload={"cycle": 1},
    )
    result_b = emit_pipeline_event(
        pipeline_id="pipe-a",
        event_type="opcua_signal",
        source="manual",
        payload={"cycle": 1},
    )

    assert result_a.created is True
    assert result_b.created is False
    assert result_a.event_id == result_b.event_id
    assert result_a.dedupe_key == result_b.dedupe_key

    store = PipelineStateStore(
        db_path=semantics_dir / "cf-pipeline-states.duckdb",
        semantics_dir=semantics_dir,
    )
    con = store._connect(read_only=True)
    try:
        row = con.execute("SELECT COUNT(*) FROM run_events").fetchone()
    finally:
        con.close()
    assert row is not None
    assert int(row[0]) == 1


def test_emit_pipeline_event_respects_explicit_dedupe_key(monkeypatch, tmp_path: Path) -> None:
    semantics_dir = tmp_path / "semantics"
    semantics_dir.mkdir(parents=True, exist_ok=True)
    _patch_gatekeeper_ontology_manager(monkeypatch, semantics_dir)

    result_a = emit_pipeline_event(
        pipeline_id="pipe-a",
        event_type="manual",
        source="external",
        payload={"x": 1},
        dedupe_key="fixed-key",
    )
    result_b = emit_pipeline_event(
        pipeline_id="pipe-a",
        event_type="manual",
        source="external",
        payload={"x": 999},
        dedupe_key="fixed-key",
    )
    assert result_a.created is True
    assert result_b.created is False
    assert result_a.event_id == result_b.event_id
