import pytest

from pypos.modules.sinkronisasi.services.event_first_export_runtime_service import (
    EventFirstExportRuntimeService,
)
from pypos.modules.sinkronisasi.services.event_ingestion_backpressure_service import (
    EventIngestionBackpressureService,
)
from pypos.modules.sinkronisasi.services.event_ingestion_gateway_service import (
    EventIngestionGatewayService,
)
from pypos.modules.sinkronisasi.services.event_outbox_service import EventOutboxService

# edited by glg
pytestmark = [pytest.mark.unit]


def _build_runtime_service(tmp_path):
    db_path = str(tmp_path / "event_first_runtime.db")
    outbox = EventOutboxService(db_path=db_path)
    gateway = EventIngestionGatewayService(
        outbox_service=outbox,
        backpressure_service=EventIngestionBackpressureService(
            {
                "default_batch": 10,
                "min_batch": 1,
                "pending_warn": 1000,
                "pending_critical": 10000,
            }
        ),
    )
    return EventFirstExportRuntimeService(gateway_service=gateway, outbox_service=outbox)


def test_event_first_runtime_enqueue_drain_dan_jalankan_cycle(tmp_path):
    service = _build_runtime_service(tmp_path)
    calls = []

    def _handler(**kwargs):
        calls.append(dict(kwargs or {}))
        return {
            "ran": True,
            "exported_rows": 3,
            "uploaded": 2,
            "failed": 0,
            "retried": 0,
            "cleaned_files": 0,
            "cleaned_empty_flux": 0,
            "retry_items": [],
            "failed_items": [],
            "requeued_failed_transient": 0,
            "suppressed_direct_only": 0,
            "suppressed_table_toggle": 0,
        }

    result = service.run_trigger_cycle(
        source="timer",
        source_id="M-01",
        cycle_handler=_handler,
        metrics={"pending": 0, "inflight": 0, "error_rate_pct": 0.0, "avg_latency_ms": 10.0},
        dedup_window_seconds=5,
        claim_limit=10,
    )

    assert int(result["enqueue"].get("ingested_count") or 0) >= 1
    assert int(result["drain"].get("processed") or 0) >= 1
    assert result["cycle_result"]["ran"] is True
    assert int(result["cycle_result"]["exported_rows"] or 0) == 3
    assert len(calls) == 1


def test_event_first_runtime_dedup_mencegah_trigger_duplikat_dalam_window(tmp_path):
    service = _build_runtime_service(tmp_path)

    def _handler(**_kwargs):
        return {"ran": True, "exported_rows": 1, "uploaded": 0, "failed": 0, "retried": 0}

    first = service.run_trigger_cycle(
        source="timer",
        source_id="M-02",
        cycle_handler=_handler,
        metrics={"pending": 0, "inflight": 0, "error_rate_pct": 0.0, "avg_latency_ms": 10.0},
        dedup_window_seconds=3600,
        purge_sent_days=365,
    )
    second = service.run_trigger_cycle(
        source="timer",
        source_id="M-02",
        cycle_handler=_handler,
        metrics={"pending": 0, "inflight": 0, "error_rate_pct": 0.0, "avg_latency_ms": 10.0},
        dedup_window_seconds=3600,
        purge_sent_days=365,
    )

    assert first["cycle_result"]["ran"] is True
    assert int(second["enqueue"].get("duplicate_count") or 0) >= 1
    assert second["cycle_result"]["ran"] is False
    assert str(second["cycle_result"].get("skipped") or "") == "event_dedup_duplicate"


def test_event_first_runtime_mark_fail_saat_handler_error(tmp_path):
    service = _build_runtime_service(tmp_path)

    def _handler(**_kwargs):
        raise RuntimeError("simulasi gagal proses event")

    result = service.run_trigger_cycle(
        source="timer",
        source_id="M-03",
        cycle_handler=_handler,
        metrics={"pending": 0, "inflight": 0, "error_rate_pct": 0.0, "avg_latency_ms": 10.0},
        dedup_window_seconds=5,
        claim_limit=5,
        retry_delay_seconds=5,
    )

    assert int(result["drain"].get("failed") or 0) >= 1
    assert result["cycle_result"]["ran"] is False
