from neurovault.engine import NeurovaultEngine


def test_init_and_ingest(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    mem = v.ingest("User prefers dark mode", source="user", importance=0.8)
    assert mem is not None
    assert mem.content == "User prefers dark mode"


def test_recall_keyword(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Python is the best language", source="user")
    v.ingest("React is great for frontends", source="user")
    results = v.recall("Python", mode="keyword")
    assert len(results) > 0
    assert "Python" in results[0].memory.content


def test_entities_after_ingest(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Alex built a Python service for Project Alpha", source="user")
    entities = v.get_entities(limit=20)
    names = {e.name.lower() for e in entities}
    assert "python" in names


def test_status(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Test memory")
    s = v.status
    assert s.entity_count >= 0
    assert s.db_size_bytes > 0


def test_consolidation_entities_phase(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    for i in range(5):
        v.ingest(f"Working on Python project with Alex #{i}", source="user", importance=0.8)
    report = v.consolidate(force=True, phases=["entities"])
    assert report.entities_extracted > 0
    assert report.relationships_found >= 0


def test_predict_from_context(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Fix auth_handler.py token refresh bug in Python service", source="user", importance=0.9)
    v.ingest("Discuss auth refactor for Project Alpha with Alex", source="user", importance=0.8)
    v.update_context("active_file", "auth_handler.py")
    predictions = v.predict()
    assert len(predictions) >= 1


def test_recall_by_entity_and_graph(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Alex is using Python for Project Alpha", source="user")
    entity_results = v.recall_by_entity("Alex", limit=5)
    assert len(entity_results) >= 1

    graph_results = v.recall_by_graph("Alex", depth=2, limit=5)
    assert isinstance(graph_results, list)

    graph = v.get_entity_graph("Alex", depth=2)
    assert graph is not None
    assert graph.root_entity.name.lower().startswith("alex")


def test_audit_export_import(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Export this memory", source="user")

    audit = v.audit(format="dict")
    assert audit["active_memories"] >= 1

    export_path = tmp_path / "export.json"
    v.export(str(export_path), format="json")
    assert export_path.exists()

    v2 = NeurovaultEngine.init("test2", no_embedding=True, base_dir=str(tmp_path))
    merge = v2.import_memories(str(export_path))
    assert merge.imported >= 1


def test_chronos_native_surfaces(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Alice uses Python for Chronos integration", source="user", importance=0.7)

    stats = v.run_decay()
    assert isinstance(stats, dict)
    assert "updated" in stats or "archived" in stats

    events = v.list_chronos_events(limit=10)
    assert isinstance(events, list)

    plugins = v.list_chronos_plugins()
    assert isinstance(plugins, list)

    rebuilt = v.rebuild_chronos_graph()
    assert isinstance(rebuilt, bool)

    neighbors = v.get_chronos_graph_neighbors("Alice")
    assert isinstance(neighbors, list)

    path = v.get_chronos_graph_path("Alice", "Python")
    assert isinstance(path, list)


def test_persona_merge_flow(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Default timeline note", source="user")

    persona = v.create_persona("work")
    assert persona["name"] == "work"

    assert v.use_persona("work") is True
    v.ingest("Work milestone due Friday", source="user")

    assert v.use_persona("default") is True
    dry = v.merge_persona("work", strategy="additive", dry_run=True)
    assert dry["dry_run"] is True
    assert dry["added"] >= 1

    live = v.merge_persona("work", strategy="additive", dry_run=False)
    assert live["added"] >= 1

    recall = v.recall("milestone", mode="keyword", limit=5)
    assert len(recall) >= 1


def test_checkpoint_diff_milestone_revert(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("First checkpoint memory", source="user")
    cp1 = v.checkpoint("cp1")

    v.ingest("Second checkpoint memory", source="user")
    cp2 = v.checkpoint("cp2")

    diff = v.checkpoint_diff(cp1.id.hex()[:8], cp2.id.hex()[:8], limit=10)
    assert isinstance(diff, dict)
    assert len(diff["added"]) >= 1

    milestone = v.create_milestone("before-second", ref=cp1.id.hex()[:8], message="pre second")
    assert milestone["name"] == "before-second"

    milestones = v.list_milestones(limit=20)
    assert any(item["name"] == "before-second" for item in milestones)

    reverted = v.revert("before-second", hard=False)
    assert not reverted.get("error")


def test_sync_key_verify_surfaces(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Integrity probe memory", source="user")

    verify = v.verify_integrity()
    assert isinstance(verify, dict)
    assert "memories_checked" in verify

    key = v.generate_key("default")
    assert isinstance(key, dict)
    assert "error" in key

    out_path = tmp_path / "bundle.csf"
    push = v.sync_push("peer-demo", str(out_path))
    assert isinstance(push, dict)
    assert "error" in push

    pull = v.sync_pull(str(out_path), strategy="newer_wins")
    assert isinstance(pull, dict)
    assert "error" in pull


def test_v02_stats_plugin_and_hierarchy_surfaces(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("Python and FastAPI power Project Atlas", source="user")

    stats = v.stats()
    assert isinstance(stats, dict)
    assert "memory_count" in stats
    assert "pipeline" in stats

    sensors = v.sensor_status()
    assert isinstance(sensors, dict)
    assert "clipboard" in sensors

    plugin_cfg = v.set_plugin_config("demo-plugin", {"token": "abc"})
    assert plugin_cfg is True
    assert v.enable_plugin("demo-plugin") is True
    plugins = v.list_plugins()
    assert isinstance(plugins, list)
    assert any(item["id"] == "demo-plugin" for item in plugins)

    entity = v.get_entities(limit=1)[0]
    hierarchy = v.get_entity_hierarchy(entity.id)
    assert isinstance(hierarchy, dict)
    assert "entity" in hierarchy

    concepts = v.get_concept_tree(limit=10)
    assert isinstance(concepts, list)


def test_ontology_expanded_multi_signal_recall(tmp_path):
    v = NeurovaultEngine.init("test", no_embedding=True, base_dir=str(tmp_path))
    v.ingest("We migrated analytics workloads to postgresql this week.", source="user")

    keyword_results = v.recall("database", mode="keyword", limit=5)
    assert isinstance(keyword_results, list)
    assert len(keyword_results) == 0

    multi_results = v.recall("database", mode="multi", limit=5)
    assert len(multi_results) >= 1
    assert any("postgresql" in r.memory.content.lower() for r in multi_results)


def test_v03_delta_bundle_roundtrip(tmp_path):
    source = NeurovaultEngine.init("source", no_embedding=True, base_dir=str(tmp_path))
    target = NeurovaultEngine.init("target", no_embedding=True, base_dir=str(tmp_path))

    source.ingest("Cross-device sync memory: discuss federation protocol.", source="user", importance=0.8)

    hnsw = source.hnsw_status()
    assert "enabled" in hnsw

    bundle = source.create_delta_bundle()
    assert isinstance(bundle, dict)
    assert len(bundle.get("memories", [])) >= 1

    applied = target.apply_delta_bundle(bundle)
    assert isinstance(applied, dict)
    assert applied.get("memories_added", 0) + applied.get("memories_merged", 0) >= 1

    synced_rows = target._nv.get_memories_since_checkpoint(None)
    assert any("federation protocol" in row.get("content", "").lower() for row in synced_rows)
