from __future__ import annotations

import sqlite3

import eightmem.services.memory_service as memory_service
from eightmem.services.memory_service import (
    apply_memory_updates,
    build_compare_output,
    build_base_context,
    build_passport_summary,
    build_recent_changes_summary,
    build_structured_memory_summary,
    export_context_text,
    find_duplicate_active_memory,
    get_user_memory_dir,
    load_dashboard_payload,
    reset_memory_runtime,
    save_file_content,
)
from eightmem.services.telegram_service import process_telegram_message
from eightmem.core.sqlite_facts import search_semantic_memory, sqlite_vec_available


def test_user_memory_namespace_is_separate_from_default(fake_home):
    default_context = build_base_context("dashboard")
    user_mem = get_user_memory_dir("telegram-user-1")

    assert user_mem.exists()
    assert str(user_mem).startswith(str(fake_home))
    assert "/users/telegram-user-1/memory" in str(user_mem)
    assert default_context["memory_dir"] == str(fake_home / "memory")


def test_user_memory_export_and_save_are_isolated(fake_home):
    assert save_file_content(
        "PREFERENCES.md",
        "# PREFERENCES\n\nStable response and formatting preferences.\n\n## Entries\n\n- user specific preference\n",
        user_id="telegram-user-2",
    )

    exported = export_context_text(user_id="telegram-user-2")
    default_export = export_context_text()

    assert "user specific preference" in exported
    assert "user specific preference" not in default_export


def test_dashboard_timeline_records_real_memory_changes(fake_home):
    reset_memory_runtime("telegram-user-3")
    apply_memory_updates({"PREFERENCES.md": {"Keep answers concise."}}, user_id="telegram-user-3")
    apply_memory_updates({"CORRECTIONS.md": {"do not use long paragraphs"}}, user_id="telegram-user-3")

    dashboard = load_dashboard_payload("telegram-user-3")
    timeline = dashboard["timeline"]

    assert isinstance(timeline, list)
    assert any("Added to Preferences: Keep answers concise." in item for item in timeline)
    assert any("Added to Corrections: do not use long paragraphs" in item for item in timeline)


def test_dashboard_timeline_hides_stale_delete_when_memory_is_active_again(fake_home):
    process_telegram_message(user_id="telegram-user-timeline", text="remember I prefer light mode")
    process_telegram_message(user_id="telegram-user-timeline", text="forget light mode")
    process_telegram_message(user_id="telegram-user-timeline", text="forget yes")
    process_telegram_message(user_id="telegram-user-timeline", text="remember I prefer light mode")

    dashboard = load_dashboard_payload("telegram-user-timeline")
    sections = dashboard["sections"]
    timeline = dashboard["timeline"]

    assert "Prefers light mode." in sections["PREFERENCES.md"]
    assert not any('Deleted memory from Preferences: "Prefers light mode."' in item for item in timeline)
    assert not any("Forgot: Prefers light mode." in item for item in timeline)


def test_duplicate_check_ignores_deletion_audit_but_keeps_live_evolution_entries(fake_home):
    apply_memory_updates(
        {"EVOLUTION.md": {'Deleted memory from Beliefs: "my favourite drink is black tea"'}},
        user_id="telegram-user-evolution-delete-only",
    )
    assert find_duplicate_active_memory("telegram-user-evolution-delete-only", "my favourite drink is black tea") is None

    apply_memory_updates(
        {"EVOLUTION.md": {"my favourite drink is black tea"}},
        user_id="telegram-user-evolution-live",
    )
    duplicate = find_duplicate_active_memory("telegram-user-evolution-live", "my favourite drink is black tea")
    assert duplicate is not None
    assert duplicate["value"] == "my favourite drink is black tea"


def test_recent_changes_summary_prefers_current_session_after_reset(fake_home):
    process_telegram_message(user_id="telegram-user-changes", text="Remember: keep answers concise")
    process_telegram_message(user_id="telegram-user-changes", text="Correct: do not use long paragraphs")
    process_telegram_message(user_id="telegram-user-changes", text="reset memory")
    process_telegram_message(user_id="telegram-user-changes", text="Please keep my work replies concise from now on")
    process_telegram_message(user_id="telegram-user-changes", text="Don't use emojis in client messages")

    summary = build_recent_changes_summary("telegram-user-changes")

    assert "What changed in your memory:" in summary
    assert "Added preference: Keep my work replies concise from now on." in summary
    assert "Added correction: Don't use emojis in client messages." in summary
    assert "Reset: cleared saved memory and started fresh." in summary
    assert "do not use long paragraphs" not in summary


def test_recent_changes_summary_uses_human_diff_language(fake_home):
    process_telegram_message(user_id="telegram-user-human-changes", text="remember I prefer dark mode")
    process_telegram_message(user_id="telegram-user-human-changes", text="actually, I prefer light mode")
    process_telegram_message(user_id="telegram-user-human-changes", text="forget light mode")
    process_telegram_message(user_id="telegram-user-human-changes", text="forget yes")

    summary = build_recent_changes_summary("telegram-user-human-changes")

    assert "What changed in your memory:" in summary
    assert "Forgot preference: Prefers light mode." in summary
    assert "Updated memory: Prefers dark mode. -> Prefers light mode." in summary
    assert "Added preference: Prefers light mode." not in summary
    assert "Removed preference: Prefers dark mode." not in summary
    assert "PREFERENCES.md" not in summary
    assert "Deleted memory from" not in summary


def test_recent_changes_summary_hides_stale_forget_when_memory_is_active_again(fake_home):
    process_telegram_message(user_id="telegram-user-stale-changes", text="remember I prefer light mode")
    process_telegram_message(user_id="telegram-user-stale-changes", text="forget light mode")
    process_telegram_message(user_id="telegram-user-stale-changes", text="forget yes")
    process_telegram_message(user_id="telegram-user-stale-changes", text="remember I prefer light mode")

    summary = build_recent_changes_summary("telegram-user-stale-changes")

    assert "Added preference: Prefers light mode." in summary
    assert "Forgot preference: Prefers light mode." not in summary
    assert "Removed preference: Prefers light mode." not in summary


def test_sqlite_fact_store_is_created_and_mirrors_active_memory(fake_home):
    apply_memory_updates({"PREFERENCES.md": {"Keep answers concise."}}, user_id="telegram-user-4")
    apply_memory_updates({"CORRECTIONS.md": {"do not use emojis"}}, user_id="telegram-user-4")

    db_path = fake_home / "users" / "telegram-user-4" / ".memory_facts.sqlite3"
    assert db_path.exists()

    with sqlite3.connect(db_path) as conn:
        rows = conn.execute(
            """
            SELECT file_name, object_text
            FROM facts
            WHERE valid_to IS NULL
            ORDER BY file_name, object_text
            """
        ).fetchall()

    assert ("CORRECTIONS.md", "do not use emojis") in rows
    assert ("PREFERENCES.md", "Keep answers concise.") in rows


def test_sqlite_event_store_mirrors_memory_events(fake_home):
    apply_memory_updates({"PREFERENCES.md": {"Keep answers concise."}}, user_id="telegram-user-5")

    db_path = fake_home / "users" / "telegram-user-5" / ".memory_facts.sqlite3"
    with sqlite3.connect(db_path) as conn:
        events = conn.execute(
            """
            SELECT action, file_name, detail
            FROM memory_events
            ORDER BY id
            """
        ).fetchall()

    assert any(
        action == "update_file" and file_name == "PREFERENCES.md" and "Added to Preferences" in detail
        for action, file_name, detail in events
    )


def test_passport_and_recall_use_structured_fact_view(fake_home):
    process_telegram_message(user_id="telegram-user-6", text="Remember: keep answers concise")
    process_telegram_message(user_id="telegram-user-6", text="Correct: do not use emojis")

    passport = build_passport_summary("telegram-user-6")
    recall = process_telegram_message(user_id="telegram-user-6", text="What do you know about me")

    assert "Preference: Keep answers concise." in passport
    assert "Correction: do not use emojis" in passport
    assert "Preference: Keep answers concise." in recall.message
    assert "Correction: do not use emojis" in recall.message


def test_passport_empty_state_is_product_grade(fake_home):
    passport = build_passport_summary("telegram-user-empty-passport")

    assert "8mem Passport" in passport
    assert "Status: No trusted memory yet" in passport
    assert "Active entries: 0" in passport
    assert "Corrections tracked: 0" in passport
    assert "Currently using:" in passport
    assert "Nothing yet. Add one preference or correction to start shaping replies." in passport
    assert 'Try: Remember: keep answers concise' in passport


def test_passport_shows_trust_metadata_for_active_memory(fake_home):
    process_telegram_message(user_id="telegram-user-passport-trust", text="Remember: keep answers concise")
    process_telegram_message(user_id="telegram-user-passport-trust", text="Correct: do not use emojis")

    passport = build_passport_summary("telegram-user-passport-trust")

    assert "8mem Passport" in passport
    assert "Status: Stable" in passport
    assert "Active entries:" in passport
    assert "Corrections tracked: 1" in passport
    assert "Currently using:" in passport
    assert "Trust basis:" in passport
    assert "I'm also following 1 saved correction." in passport
    assert "Nothing in your saved memory conflicts right now." in passport


def test_passport_uses_temporal_location_facts(fake_home):
    content = (
        "# IDENTITY\n\n"
        "Who the user is and what they are focused on.\n\n"
        "## Entries\n\n"
        "- I moved to Singapore from London.\n"
    )
    assert save_file_content("IDENTITY.md", content, user_id="telegram-user-7")

    passport = build_passport_summary("telegram-user-7")

    assert "Current location: Singapore" in passport
    assert "Previous location: London" in passport


def test_structured_fact_correction_archives_conflicting_emoji_guidance(fake_home):
    process_telegram_message(user_id="telegram-user-8", text="Remember: use emojis")
    process_telegram_message(user_id="telegram-user-8", text="Correct: do not use emojis")

    passport = build_passport_summary("telegram-user-8")
    recall = process_telegram_message(user_id="telegram-user-8", text="What do you know about me")

    assert "Memory status: Stable" in passport
    assert "Nothing in your saved memory conflicts right now." in passport
    assert "Correction: do not use emojis" in recall.message
    assert "Preference: Use emojis." not in recall.message
    assert "Possible conflict:" not in recall.message


def test_structured_memory_summary_exposes_highlights_and_trust_basis(fake_home):
    process_telegram_message(user_id="telegram-user-9", text="Remember: keep answers concise")
    process_telegram_message(user_id="telegram-user-9", text="Correct: do not use emojis")

    summary = build_structured_memory_summary("telegram-user-9")

    assert summary["active_fact_count"] >= 2
    assert summary["derived_fact_count"] >= 2
    assert any("Emoji policy: no emojis" == item for item in summary["highlights"])
    assert any("Default detail: concise" == item for item in summary["highlights"])
    assert any("saved preference" in item for item in summary["trust_basis"])
    assert any("saved correction" in item for item in summary["trust_basis"])


def test_sqlite_vec_indexes_and_finds_related_memory(fake_home):
    if not sqlite_vec_available():
        pytest.skip("sqlite-vec optional dependency is not installed")

    process_telegram_message(user_id="telegram-user-10", text="Remember: keep my work replies concise")
    process_telegram_message(user_id="telegram-user-10", text="Correct: do not use emojis")

    mem = get_user_memory_dir("telegram-user-10")
    matches = search_semantic_memory(mem, "reply to a work client with a concise answer", k=3)

    texts = [item["text"] for item in matches]
    assert any("keep my work replies concise" in text.lower() for text in texts)


def test_sqlite_vec_search_excludes_audit_event_details(fake_home):
    if not sqlite_vec_available():
        pytest.skip("sqlite-vec optional dependency is not installed")

    process_telegram_message(user_id="telegram-user-11", text="Remember: prefer calm launch summaries")
    process_telegram_message(user_id="telegram-user-11", text="forget calm launch summaries")
    process_telegram_message(user_id="telegram-user-11", text="forget yes")

    mem = get_user_memory_dir("telegram-user-11")
    matches = search_semantic_memory(mem, "deleted calm launch summary preference", k=5)

    texts = [str(item["text"]).lower() for item in matches]
    assert not any("deleted memory" in text for text in texts)
    assert not any("calm launch summaries" in text for text in texts)


def test_compare_basis_prefers_semantic_memory_matches(fake_home, monkeypatch):
    process_telegram_message(
        user_id="telegram-user-compare-semantic",
        text="remember I prefer investor offers evaluated against control, dilution, and long-term ownership",
    )

    monkeypatch.setattr(
        memory_service,
        "search_semantic_memory",
        lambda memory_dir, query, *, k=3: [
            {
                "kind": "fact",
                "source_key": "PREFERENCES.md:preference.default",
                "text": "Prefers investor offers evaluated against control, dilution, and long-term ownership.",
                "distance": 0.12,
            }
        ],
    )
    monkeypatch.setattr(memory_service, "generate_generic_reply", lambda *args, **kwargs: "A generic funding proposal response.")
    monkeypatch.setattr(
        memory_service,
        "generate_memory_aware_reply",
        lambda *args, **kwargs: "Evaluate the offer against control, dilution, and long-term ownership.",
    )

    output = build_compare_output("telegram-user-compare-semantic", "funding proposal")

    assert "What changed because of memory:" in output
    assert "- Preference: Prefers investor offers evaluated against control, dilution, and long-term ownership" in output
