from __future__ import annotations

from datetime import datetime, timezone

import pytest

from agent_framework.idempotency import IdempotencyStore
from agent_framework.cache.cache import InMemoryCache
from agent_framework.persistence.oracle_store import OracleStore


class BrokenCache:
    async def get(self, key):
        raise RuntimeError("db unavailable")

    async def set(self, key, value, ttl_seconds=None):
        raise RuntimeError("db unavailable")

    async def delete(self, key):
        raise RuntimeError("db unavailable")


def test_oracle_cache_datetime_normalization_accepts_naive_and_aware():
    naive = datetime(2026, 8, 31, 21, 0, 0)
    aware = datetime(2026, 8, 31, 21, 0, 0, tzinfo=timezone.utc)

    normalized_naive = OracleStore._normalize_datetime_for_compare(naive)
    normalized_aware = OracleStore._normalize_datetime_for_compare(aware)

    assert normalized_naive.tzinfo is not None
    assert normalized_aware.tzinfo is not None
    assert normalized_naive == normalized_aware
    assert not (normalized_naive < normalized_aware)


@pytest.mark.asyncio
async def test_idempotency_fail_open_uses_memory_fallback_when_primary_fails():
    fallback = InMemoryCache()
    store = IdempotencyStore(
        BrokenCache(),
        namespace="contas",
        ttl_seconds=60,
        fallback_backend=fallback,
        fail_open=True,
    )

    assert await store.get("operation") is None
    await store.set("operation", {"success": True})
    assert await store.get("operation") == {"success": True}
    await store.delete("operation")
    assert await store.get("operation") is None


@pytest.mark.asyncio
async def test_idempotency_fail_closed_preserves_durable_semantics():
    store = IdempotencyStore(BrokenCache(), namespace="strict", fail_open=False)
    with pytest.raises(RuntimeError, match="db unavailable"):
        await store.get("operation")
