"""Tests for the Sovereign Memory Store and Promotion system.

Zero-API, zero-network — everything runs against an in-memory SQLite DB.
"""

import os
import sys
import tempfile
import time
import unittest

# Allow imports from parent package
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from sovereign_store import (
    SovereignStore,
    encode_text,
    cosine_similarity_phase,
    serialize_vector,
    deserialize_vector,
)
from promotion import (
    evaluate_for_promotion,
    promote,
    promote_session_batch,
)


class TestHRRVectors(unittest.TestCase):
    """Test the HRR encoding and similarity functions."""

    def test_deterministic_encoding(self):
        """Same text always produces the same vector."""
        v1 = encode_text("hello world")
        v2 = encode_text("hello world")
        self.assertAlmostEqual(cosine_similarity_phase(v1, v2), 1.0, places=5)

    def test_similar_texts_higher_similarity(self):
        """Related texts should be more similar than unrelated ones."""
        v_agent = encode_text("agent memory palace retrieval")
        v_similar = encode_text("agent recall memory search")
        v_unrelated = encode_text("banana strawberry fruit smoothie")
        sim_related = cosine_similarity_phase(v_agent, v_similar)
        sim_unrelated = cosine_similarity_phase(v_agent, v_unrelated)
        self.assertGreater(sim_related, sim_unrelated)

    def test_serialize_roundtrip(self):
        """Vectors survive serialization to/from bytes."""
        vec = encode_text("test serialization")
        blob = serialize_vector(vec)
        restored = deserialize_vector(blob)
        sim = cosine_similarity_phase(vec, restored)
        self.assertAlmostEqual(sim, 1.0, places=5)

    def test_empty_text(self):
        """Empty text gets a fallback encoding."""
        vec = encode_text("")
        self.assertEqual(len(vec) if hasattr(vec, '__len__') else len(list(vec)), 512)


class TestSovereignStore(unittest.TestCase):
    """Test the SQLite-backed sovereign store."""

    def setUp(self):
        self.db_path = os.path.join(tempfile.mkdtemp(), "test.db")
        self.store = SovereignStore(db_path=self.db_path)

    def tearDown(self):
        self.store.close()
        if os.path.exists(self.db_path):
            os.remove(self.db_path)

    def test_store_and_retrieve(self):
        """Store a fact and find it via search."""
        mid = self.store.store("Timmy is a sovereign AI agent on Hermes VPS", room="identity")
        results = self.store.search("sovereign agent", room="identity")
        self.assertTrue(any(r["memory_id"] == mid for r in results))

    def test_fts_search(self):
        """FTS5 keyword search works."""
        self.store.store("The beacon game uses paperclips mechanics", room="projects")
        self.store.store("Fleet agents handle delegation and dispatch", room="fleet")
        results = self.store.search("paperclips")
        self.assertTrue(len(results) > 0)
        self.assertIn("paperclips", results[0]["content"].lower())

    def test_hrr_search_semantic(self):
        """HRR similarity finds related content even without exact keywords."""
        self.store.store("Memory palace rooms organize facts spatially", room="memory")
        self.store.store("Pizza delivery service runs on weekends", room="unrelated")
        results = self.store.search("organize knowledge rooms", room="memory")
        self.assertTrue(len(results) > 0)
        self.assertIn("palace", results[0]["content"].lower())

    def test_room_filtering(self):
        """Room filter restricts search scope."""
        self.store.store("Hermes harness manages tool calls", room="infrastructure")
        self.store.store("Hermes mythology Greek god", room="lore")
        results = self.store.search("Hermes", room="infrastructure")
        self.assertTrue(all(r["room"] == "infrastructure" for r in results))

    def test_trust_boost(self):
        """Trust score increases when boosted."""
        mid = self.store.store("fact", trust=0.5)
        self.store.boost_trust(mid, delta=0.1)
        results = self.store.room_contents("general")
        fact = next(r for r in results if r["memory_id"] == mid)
        self.assertAlmostEqual(fact["trust_score"], 0.6, places=2)

    def test_trust_decay(self):
        """Trust score decreases when decayed."""
        mid = self.store.store("questionable fact", trust=0.5)
        self.store.decay_trust(mid, delta=0.2)
        results = self.store.room_contents("general")
        fact = next(r for r in results if r["memory_id"] == mid)
        self.assertAlmostEqual(fact["trust_score"], 0.3, places=2)

    def test_batch_store(self):
        """Batch store works."""
        ids = self.store.store_batch([
            {"content": "fact one", "room": "test"},
            {"content": "fact two", "room": "test"},
            {"content": "fact three", "room": "test"},
        ])
        self.assertEqual(len(ids), 3)
        rooms = self.store.list_rooms()
        test_room = next(r for r in rooms if r["room"] == "test")
        self.assertEqual(test_room["count"], 3)

    def test_stats(self):
        """Stats returns correct counts."""
        self.store.store("a fact", room="r1")
        self.store.store("another fact", room="r2")
        s = self.store.stats()
        self.assertEqual(s["total"], 2)
        self.assertEqual(s["room_count"], 2)

    def test_retrieval_count_increments(self):
        """Retrieval count goes up when a fact is found via search."""
        self.store.store("unique searchable content xyz123", room="test")
        self.store.search("xyz123")
        results = self.store.room_contents("test")
        self.assertTrue(any(r["retrieval_count"] > 0 for r in results))


class TestPromotion(unittest.TestCase):
    """Test the quality-gated promotion system."""

    def setUp(self):
        self.db_path = os.path.join(tempfile.mkdtemp(), "promo_test.db")
        self.store = SovereignStore(db_path=self.db_path)

    def tearDown(self):
        self.store.close()

    def test_successful_promotion(self):
        """Good content passes all gates."""
        result = promote(
            content="Timmy runs on the Hermes VPS at 143.198.27.163 with local Ollama inference",
            store=self.store,
            session_id="test-session-001",
            scratch_key="vps_info",
            room="infrastructure",
        )
        self.assertTrue(result.success)
        self.assertIsNotNone(result.memory_id)

    def test_reject_too_short(self):
        """Short fragments get rejected."""
        result = promote(
            content="yes",
            store=self.store,
            session_id="test",
            scratch_key="short",
        )
        self.assertFalse(result.success)
        self.assertIn("Too short", result.reason)

    def test_reject_duplicate(self):
        """Duplicate content gets rejected."""
        self.store.store("SOUL.md is the canonical identity document for Timmy", room="identity")
        result = promote(
            content="SOUL.md is the canonical identity document for Timmy",
            store=self.store,
            session_id="test",
            scratch_key="soul",
            room="identity",
        )
        self.assertFalse(result.success)
        self.assertIn("uplicate", result.reason)

    def test_reject_stale(self):
        """Old notes get flagged as stale."""
        old_time = time.time() - (86400 * 10)
        result = promote(
            content="This is a note from long ago about something important",
            store=self.store,
            session_id="test",
            scratch_key="old",
            written_at=old_time,
        )
        self.assertFalse(result.success)
        self.assertIn("Stale", result.reason)

    def test_force_bypasses_gates(self):
        """Force flag overrides quality gates."""
        result = promote(
            content="ok",
            store=self.store,
            session_id="test",
            scratch_key="forced",
            force=True,
        )
        self.assertTrue(result.success)

    def test_evaluate_dry_run(self):
        """Evaluate returns gate details without promoting."""
        eval_result = evaluate_for_promotion(
            content="The fleet uses kimi-k2.5 as the primary model for all agent operations",
            store=self.store,
            room="fleet",
        )
        self.assertTrue(eval_result["eligible"])
        self.assertTrue(all(p for p, _ in eval_result["gates"].values()))

    def test_batch_promotion(self):
        """Batch promotion processes all notes."""
        notes = {
            "infra": {"value": "Hermes VPS runs Ubuntu 22.04 with 2 vCPUs and 4GB RAM", "written_at": time.strftime("%Y-%m-%d %H:%M:%S")},
            "short": {"value": "no", "written_at": time.strftime("%Y-%m-%d %H:%M:%S")},
            "model": {"value": "The primary local model is gemma4:latest running on Ollama", "written_at": time.strftime("%Y-%m-%d %H:%M:%S")},
        }
        results = promote_session_batch(self.store, "batch-session", notes, room="config")
        promoted = [r for r in results if r.success]
        rejected = [r for r in results if not r.success]
        self.assertEqual(len(promoted), 2)
        self.assertEqual(len(rejected), 1)

    def test_promotion_logged(self):
        """Successful promotions appear in the audit log."""
        promote(
            content="Forge is hosted at forge.alexanderwhitestone.com running Gitea",
            store=self.store,
            session_id="log-test",
            scratch_key="forge",
            room="infrastructure",
        )
        log = self.store.recent_promotions()
        self.assertTrue(len(log) > 0)
        self.assertEqual(log[0]["session_id"], "log-test")
        self.assertEqual(log[0]["scratch_key"], "forge")


if __name__ == "__main__":
    unittest.main()
