"""Tests for CredentialStoreBase TTL boundary conditions and remove parameter.

Complements test_credential_store_base.py and test_credential_store_extended.py
by testing precise TTL boundaries using mocked time.time().
"""

import time as time_module
from unittest.mock import patch


from local_deep_research.database.credential_store_base import (
    CredentialStoreBase,
)


class ConcreteStore(CredentialStoreBase):
    """Minimal concrete subclass for testing base class logic."""

    def store(self, key, username, password):
        self._store_credentials(
            key, {"username": username, "password": password}
        )

    def retrieve(self, key, remove=False):
        return self._retrieve_credentials(key, remove=remove)


# ---------------------------------------------------------------------------
# _retrieve_credentials boundary conditions
# ---------------------------------------------------------------------------


class TestRetrieveCredentialsBoundary:
    """TTL boundary tests using mocked time."""

    def test_not_expired_at_exact_boundary(self):
        """time.time() == expires_at → NOT expired (uses > not >=)."""
        store = ConcreteStore(ttl_seconds=100)
        with patch.object(time_module, "time", return_value=1000.0):
            store.store("k", "user", "pass")
            # expires_at = 1000.0 + 100 = 1100.0

        with patch.object(time_module, "time", return_value=1100.0):
            result = store.retrieve("k")
            assert result == ("user", "pass")

    def test_expired_just_past_boundary(self):
        """time.time() = expires_at + epsilon → expired."""
        store = ConcreteStore(ttl_seconds=100)
        with patch.object(time_module, "time", return_value=1000.0):
            store.store("k", "user", "pass")

        with patch.object(time_module, "time", return_value=1100.001):
            result = store.retrieve("k")
            assert result is None

    def test_expired_entry_deleted_from_store(self):
        """Expired retrieval deletes the entry."""
        store = ConcreteStore(ttl_seconds=10)
        with patch.object(time_module, "time", return_value=1000.0):
            store.store("k", "user", "pass")

        with patch.object(time_module, "time", return_value=1011.0):
            store.retrieve("k")  # triggers deletion
        assert "k" not in store._store


# ---------------------------------------------------------------------------
# _cleanup_expired
# ---------------------------------------------------------------------------


class TestCleanupExpired:
    """Tests for _cleanup_expired logic with mocked time."""

    def test_some_expired(self):
        store = ConcreteStore(ttl_seconds=100)
        with patch.object(time_module, "time", return_value=1000.0):
            store.store("old", "u1", "p1")
        # Insert "new" directly, bypassing _store_credentials' implicit cleanup
        with patch.object(time_module, "time", return_value=2000.0):
            store._store["new"] = {
                "username": "u2",
                "password": "p2",
                "expires_at": time_module.time() + store.ttl,  # 2100
            }
            store._cleanup_expired()
        assert "old" not in store._store
        assert "new" in store._store

    def test_all_expired(self):
        store = ConcreteStore(ttl_seconds=10)
        with patch.object(time_module, "time", return_value=1000.0):
            store.store("a", "u1", "p1")
            store.store("b", "u2", "p2")

        with patch.object(time_module, "time", return_value=2000.0):
            store._cleanup_expired()
        assert len(store._store) == 0

    def test_empty_store_no_error(self):
        store = ConcreteStore(ttl_seconds=3600)
        store._cleanup_expired()  # should not raise
        assert len(store._store) == 0
