"""Tests for timer-based engine flush behavior."""

from __future__ import annotations

import pathlib
import sys
import time
import unittest


ROOT = pathlib.Path(__file__).resolve().parents[1]
SRC = ROOT / "src"
if str(SRC) not in sys.path:
    sys.path.insert(0, str(SRC))

from vedatrace._engine import Engine
from vedatrace.config import BatchingConfig, VedaTraceConfig
from vedatrace.models import LogLevel, LogRecord


class RecordingTransport:
    def __init__(self) -> None:
        self.batches: list[list[LogRecord]] = []

    def emit(self, records: list[LogRecord]) -> None:
        self.batches.append(list(records))

    def close(self) -> None:
        return None


class FailingTransport:
    def emit(self, records: list[LogRecord]) -> None:
        raise RuntimeError("timer transport failure")

    def close(self) -> None:
        return None


def _make_record(message: str) -> LogRecord:
    return LogRecord.create(
        level=LogLevel.INFO,
        message=message,
        service="orders",
        timestamp="2026-02-19T17:42:47.957694Z",
        metadata={},
    )


def _wait_until(
    predicate,
    *,
    timeout_seconds: float = 1.0,
    poll_interval_seconds: float = 0.01,
) -> bool:
    deadline = time.monotonic() + timeout_seconds
    while time.monotonic() < deadline:
        if predicate():
            return True
        time.sleep(poll_interval_seconds)
    return predicate()


class TestBatchingEngineTimer(unittest.TestCase):
    def test_timer_flushes_record_before_threshold(self) -> None:
        transport = RecordingTransport()
        config = VedaTraceConfig(
            api_key="k",
            service="s",
            batching=BatchingConfig(
                enabled=True,
                batch_size=10,
                flush_interval_seconds=0.05,
            ),
            transports=[transport],
        )
        engine = Engine(config)
        try:
            engine.emit(_make_record("one"))

            flushed = _wait_until(lambda: len(transport.batches) >= 1, timeout_seconds=1.0)

            self.assertTrue(flushed)
            self.assertEqual(len(transport.batches[0]), 1)
            self.assertEqual(transport.batches[0][0].message, "one")
        finally:
            engine.close()

    def test_close_stops_future_timer_flushes_and_post_close_emit_is_noop(self) -> None:
        transport = RecordingTransport()
        config = VedaTraceConfig(
            api_key="k",
            service="s",
            batching=BatchingConfig(
                enabled=True,
                batch_size=10,
                flush_interval_seconds=0.05,
            ),
            transports=[transport],
        )
        engine = Engine(config)
        engine.close()

        engine.emit(_make_record("after-close"))
        emitted = _wait_until(lambda: len(transport.batches) > 0, timeout_seconds=0.2)

        self.assertFalse(emitted)
        self.assertEqual(len(transport.batches), 0)

    def test_timer_flush_failures_are_swallowed_and_reported_to_on_error(self) -> None:
        seen: list[Exception] = []

        def on_error(exc: Exception) -> None:
            seen.append(exc)

        config = VedaTraceConfig(
            api_key="k",
            service="s",
            batching=BatchingConfig(
                enabled=True,
                batch_size=10,
                flush_interval_seconds=0.05,
            ),
            transports=[FailingTransport()],
            on_error=on_error,
        )
        engine = Engine(config)
        try:
            engine.emit(_make_record("will-fail-on-timer-flush"))

            reported = _wait_until(lambda: len(seen) >= 1, timeout_seconds=1.0)

            self.assertTrue(reported)
            self.assertIsInstance(seen[0], RuntimeError)
        finally:
            engine.close()
