import sqlite3
import time
from datetime import datetime, timedelta

import pytest

from pypos.modules.sinkronisasi.services.event_outbox_service import EventOutboxService

# edited by glg
pytestmark = [pytest.mark.perf_smoke, pytest.mark.non_functional]


def _force_retry_ready(db_path: str, ids):
    valid_ids = [int(x) for x in (ids or []) if int(x) > 0]
    if not valid_ids:
        return
    now_text = (datetime.utcnow() - timedelta(seconds=1)).strftime("%Y-%m-%d %H:%M:%S")
    placeholders = ",".join(["?"] * len(valid_ids))
    conn = sqlite3.connect(db_path)
    cur = conn.cursor()
    cur.execute(
        f"""
        UPDATE event_outbox
        SET available_at = ?
        WHERE id IN ({placeholders})
        """,
        (now_text, *valid_ids),
    )
    conn.commit()
    conn.close()


def test_event_outbox_backlog_burst_perf_smoke(tmp_path):
    db_path = str(tmp_path / "outbox_perf.db")
    service = EventOutboxService(db_path=db_path)

    total_events = 320
    started = time.perf_counter()
    for idx in range(total_events):
        res = service.enqueue_event(
            topic="sync.transaksi",
            payload={"id": idx, "nomer": f"INV-{idx:05d}"},
            source_id="CABANG-01",
        )
        assert bool(res.get("dedup_key"))

    claimed_total = 0
    failed_ids = []
    while True:
        batch = service.claim_batch(limit=64, lease_seconds=30, max_inflight=5000)
        if not batch:
            break
        ids = [int(item["id"]) for item in batch]
        fail_batch = ids[::7]
        fail_set = set(fail_batch)
        ack_batch = [x for x in ids if x not in fail_set]
        if fail_batch:
            service.fail_events(fail_batch, error_code="TRANSIENT", error_message="perf_test", retry_delay_seconds=5)
            failed_ids.extend(fail_batch)
        if ack_batch:
            service.ack_events(ack_batch)
        claimed_total += len(ids)

    assert claimed_total == total_events

    if failed_ids:
        _force_retry_ready(db_path, failed_ids)
        while True:
            batch = service.claim_batch(limit=128, lease_seconds=30, max_inflight=5000)
            if not batch:
                break
            service.ack_events([int(item["id"]) for item in batch])

    metrics = service.get_queue_metrics()
    elapsed_ms = (time.perf_counter() - started) * 1000.0

    assert int(metrics.get("pending") or 0) == 0
    assert int(metrics.get("inflight") or 0) == 0
    assert int(metrics.get("sent") or 0) >= total_events
    assert elapsed_ms < 15000.0
