from __future__ import annotations

from pathlib import Path

from typer.testing import CliRunner

from eightmem.cli.main import app
from eightmem.core.import_cache import create_import_cache_key, load_import_cache
from eightmem.core.ingestion import (
    build_candidate_review,
    build_import_preview,
    chunk_import_text,
    prepare_import_candidates,
    prepare_import_document,
)


runner = CliRunner()


def test_chunk_import_text_preserves_overlap_and_order(fake_home):
    text = (
        "Sentence one explains the first topic clearly. "
        "Sentence two adds more detail about the same theme. "
        "Sentence three introduces a new angle for the import pipeline. "
        "Sentence four closes the example with a final note."
    )

    chunks = chunk_import_text(text, max_words=12, overlap_words=4)

    assert len(chunks) >= 2
    assert chunks[0].chunk_idx == 0
    assert chunks[1].chunk_idx == 1
    assert chunks[0].text.startswith("Sentence one")
    assert "Sentence two" in chunks[1].text or "Sentence three" in chunks[1].text
    assert chunks[0].word_count > 0
    assert chunks[1].word_count > 0


def test_prepare_import_document_uses_local_cache(fake_home):
    text = (
        "This is a long import source for 8mem. "
        "It should be chunked and cached locally for later ingestion."
    )

    document, from_cache = prepare_import_document(
        text,
        source_kind="note",
        source_id="note-1",
        max_words=8,
        overlap_words=2,
    )

    assert from_cache is False
    assert len(document.chunks) >= 2

    cached, from_cache_again = prepare_import_document(
        text,
        source_kind="note",
        source_id="note-1",
        max_words=8,
        overlap_words=2,
    )

    assert from_cache_again is True
    assert cached.source_id == "note-1"
    assert len(cached.chunks) == len(document.chunks)

    key = create_import_cache_key(
        text,
        source_kind="note",
        source_id="note-1",
        options={"max_words": 8, "overlap_words": 2},
    )
    payload = load_import_cache(key)
    assert payload is not None
    assert payload["source_kind"] == "note"


def test_build_import_preview_summarizes_chunks(fake_home):
    text = (
        "A first sentence for import preview. "
        "A second sentence adds some more information. "
        "A third sentence makes the sample long enough to split."
    )

    document, _ = prepare_import_document(
        text,
        source_kind="text",
        source_id="preview-1",
        max_words=9,
        overlap_words=2,
    )
    preview = build_import_preview(document)

    assert preview["source_id"] == "preview-1"
    assert preview["chunk_count"] == len(document.chunks)
    assert preview["total_words"] > 0
    assert preview["chunks"][0]["preview"]


def test_prepare_import_cli_previews_chunking(fake_home, tmp_path: Path):
    sample = tmp_path / "import_sample.md"
    sample.write_text(
        (
            "This is a sample import document for 8mem. "
            "It contains enough words to require chunking for preview output. "
            "The CLI should show chunk count and whether the cache was used."
        ),
        encoding="utf-8",
    )

    result = runner.invoke(app, ["prepare-import", str(sample), "--max-words", "10", "--overlap-words", "2"])

    assert result.exit_code == 0
    assert "Prepared import:" in result.output
    assert "Chunks:" in result.output
    assert "Cache:" in result.output


def test_prepare_import_candidates_extracts_reviewable_memory_facts(fake_home):
    text = (
        "I prefer short bullet updates for work. "
        "Please avoid emojis in client replies. "
        "I moved to Singapore from London."
    )

    document, candidates, from_cache = prepare_import_candidates(
        text,
        source_kind="note",
        source_id="note-2",
        max_words=20,
        overlap_words=4,
    )

    assert from_cache is False
    assert len(document.chunks) >= 1
    texts = [item.text for item in candidates]
    assert any("Prefers short bullet updates for work." == text or "Prefers short bullet updates for work" in text for text in texts)
    assert any("Avoid emojis in client replies." == text or "Do not use emojis" in text or "Avoid emojis" in text for text in texts)
    review = build_candidate_review(candidates)
    assert "PREFERENCES.md" in review or "CORRECTIONS.md" in review


def test_extract_import_facts_cli_shows_review_candidates(fake_home, tmp_path: Path):
    sample = tmp_path / "import_facts.md"
    sample.write_text(
        (
            "I prefer short structured replies. "
            "Please avoid emojis. "
            "I am building a Telegram-first memory product."
        ),
        encoding="utf-8",
    )

    result = runner.invoke(app, ["extract-import-facts", str(sample), "--max-words", "12", "--overlap-words", "2"])

    assert result.exit_code == 0
    assert "Prepared import facts:" in result.output
    assert "Candidate facts:" in result.output
    assert "Review candidates:" in result.output
    assert "PREFERENCES.md" in result.output or "CORRECTIONS.md" in result.output or "IDENTITY.md" in result.output


def test_apply_import_facts_cli_writes_into_memory_files(fake_home, tmp_path: Path):
    sample = tmp_path / "apply_import.md"
    sample.write_text(
        (
            "I prefer short structured replies. "
            "Please avoid emojis. "
            "I am building a Telegram-first memory product."
        ),
        encoding="utf-8",
    )

    result = runner.invoke(app, ["apply-import-facts", str(sample), "--max-words", "12", "--overlap-words", "2"])

    assert result.exit_code == 0
    assert "Applied import facts:" in result.output

    prefs = (fake_home / "memory" / "PREFERENCES.md").read_text(encoding="utf-8")
    corrections = (fake_home / "memory" / "CORRECTIONS.md").read_text(encoding="utf-8")
    identity = (fake_home / "memory" / "IDENTITY.md").read_text(encoding="utf-8")

    assert "short structured replies" in prefs.lower()
    assert (
        "avoid emojis" in prefs.lower()
        or "do not use emojis" in prefs.lower()
        or "avoid emojis" in corrections.lower()
        or "do not use emojis" in corrections.lower()
    )
    assert "telegram-first memory product" in identity.lower()
