import pytest
from pathlib import Path
from runtime.memory.vector_store import VectorStore
from runtime.memory.outcome import OutcomeMemory
import datetime

@pytest.fixture
def vector_store(tmp_path):
    db_path = tmp_path / "vectors.db"
    store = VectorStore(db_path)
    yield store
    store.close()

@pytest.fixture
def outcome_memory(vector_store, tmp_path):
    return OutcomeMemory(vector_store, tmp_path)

def test_record_outcome(outcome_memory):
    doc_id = outcome_memory.record_outcome(
        action="refactor code",
        result="success",
        success=True,
        strategy="chain-of-thought",
        model="claude-3",
        latency_ms=1500,
        tokens_used=1000
    )
    assert doc_id is not None
    
    # Verify via vector store directly
    doc = outcome_memory.vector_store.get(doc_id)
    assert doc is not None
    assert doc['namespace'] == 'outcomes'
    assert doc['metadata']['success'] is True
    assert doc['metadata']['strategy'] == "chain-of-thought"

def test_get_outcomes_by_strategy(outcome_memory):
    outcome_memory.record_outcome("a1", "r1", True, strategy="s1")
    outcome_memory.record_outcome("a2", "r2", False, strategy="s1")
    outcome_memory.record_outcome("a3", "r3", True, strategy="s2")
    
    res = outcome_memory.get_outcomes_by_strategy("s1")
    assert len(res) == 2
    assert res[0]['action'] in ("a1", "a2")

def test_get_outcomes_by_model(outcome_memory):
    outcome_memory.record_outcome("a1", "r1", True, model="m1")
    outcome_memory.record_outcome("a2", "r2", False, model="m2")
    
    res = outcome_memory.get_outcomes_by_model("m1")
    assert len(res) == 1
    assert res[0]['action'] == "a1"

def test_success_rate_by_strategy(outcome_memory):
    outcome_memory.record_outcome("a1", "r1", True, strategy="s1")
    outcome_memory.record_outcome("a2", "r2", False, strategy="s1")
    outcome_memory.record_outcome("a3", "r3", True, strategy="s2")
    
    rates = outcome_memory.success_rate_by_strategy()
    assert rates["s1"] == 0.5
    assert rates["s2"] == 1.0

def test_success_rate_by_model(outcome_memory):
    outcome_memory.record_outcome("a1", "r1", True, model="m1")
    outcome_memory.record_outcome("a2", "r2", False, model="m1")
    outcome_memory.record_outcome("a3", "r3", False, model="m2")
    
    rates = outcome_memory.success_rate_by_model()
    assert rates["m1"] == 0.5
    assert rates["m2"] == 0.0

def test_detect_patterns(outcome_memory):
    for _ in range(4):
        outcome_memory.record_outcome("act", "res", True, strategy="s1", model="m1", latency_ms=100)
    for _ in range(2):
        outcome_memory.record_outcome("act", "res", True, strategy="s2", model="m2")
        
    patterns = outcome_memory.detect_patterns(min_occurrences=3)
    assert len(patterns) == 1
    assert patterns[0]['strategy'] == "s1"
    assert patterns[0]['model'] == "m1"
    assert patterns[0]['count'] == 4

def test_search_outcomes(outcome_memory):
    outcome_memory.record_outcome("fix bug in login", "fixed", True)
    outcome_memory.record_outcome("fix bug in signup", "failed", False)
    outcome_memory.record_outcome("write documentation", "done", True)
    
    res = outcome_memory.search_outcomes("bug", success_only=False)
    assert len(res) >= 2
    
    res_success = outcome_memory.search_outcomes("bug", success_only=True)
    assert len(res_success) >= 1
    for r in res_success:
        assert r['success'] is True
    # Ensure the exact match is at the top
    assert res_success[0]['result'] == "fixed"

def test_what_worked_failed(outcome_memory):
    outcome_memory.record_outcome("deploy to cloud", "success", True, strategy="s1", model="m1")
    outcome_memory.record_outcome("deploy to cloud", "crash", False, strategy="s2", model="m2")
    
    worked = outcome_memory.what_worked_for("deploy")
    assert len(worked) == 1
    assert worked[0]['strategy'] == "s1"
    
    failed = outcome_memory.what_failed_for("deploy")
    assert len(failed) == 1
    assert failed[0]['strategy'] == "s2"

def test_recommend_strategy(outcome_memory):
    # s1 works 2 times, s2 works 1 time
    outcome_memory.record_outcome("test task", "success", True, strategy="s1", model="m1")
    outcome_memory.record_outcome("test task", "success", True, strategy="s1", model="m1")
    outcome_memory.record_outcome("test task", "success", True, strategy="s2", model="m2")
    
    rec = outcome_memory.recommend_strategy("test task")
    assert rec is not None
    assert rec['strategy'] == "s1"
    assert rec['model'] == "m1"
    assert rec['based_on_count'] == 2

def test_context_for_prompt(outcome_memory):
    outcome_memory.record_outcome("install package", "success", True)
    outcome_memory.record_outcome("install package", "failure", False)
    
    ctx = outcome_memory.context_for_prompt("install")
    assert "Worked:" in ctx
    assert "Failed:" in ctx

def test_get_stats(outcome_memory):
    outcome_memory.record_outcome("a", "r", True, strategy="s1", model="m1", latency_ms=100)
    outcome_memory.record_outcome("a", "r", False, strategy="s2", model="m2", latency_ms=200)
    
    stats = outcome_memory.get_stats()
    assert stats['total_outcomes'] == 2
    assert stats['success_count'] == 1
    assert stats['failure_count'] == 1
    assert set(stats['strategies_used']) == {"s1", "s2"}
    assert stats['avg_latency_ms'] == 150

def test_prune(outcome_memory):
    outcome_memory.record_outcome("a", "r", True)
    outcome_memory.record_outcome("a", "r", False)
    
    # Manipulate created_at to be old
    c = outcome_memory.vector_store.conn.cursor()
    old_date = (datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=200)).isoformat()
    c.execute("UPDATE vectors SET created_at = ?", (old_date,))
    outcome_memory.vector_store.conn.commit()
    
    # keep_failures=True should delete only the success one
    deleted = outcome_memory.prune(older_than_days=180, keep_failures=True)
    assert deleted == 1
    
    stats = outcome_memory.get_stats()
    assert stats['total_outcomes'] == 1
    assert stats['failure_count'] == 1

def test_trend(outcome_memory):
    outcome_memory.record_outcome("a", "r", True)
    outcome_memory.record_outcome("a", "r", False)
    
    trend = outcome_memory.trend(days=7)
    assert len(trend) == 1
    assert trend[0]['total'] == 2
    assert trend[0]['successes'] == 1
    assert trend[0]['rate'] == 0.5
