import os
import sqlite3
import sys
from io import StringIO
from pathlib import Path

import lss_extract
import semantic_search as semantic_search_module
from lss_cli import main as lss_main
from lss_store import discover_files, get_db_path, ingest_many
from semantic_search import semantic_search


def _make_sqlite_db(path: Path) -> Path:
    con = sqlite3.connect(path)
    try:
        con.execute(
            "CREATE TABLE notes (id INTEGER PRIMARY KEY, title TEXT, body TEXT, meta TEXT, secret TEXT)"
        )
        con.execute(
            "INSERT INTO notes (title, body, meta, secret) VALUES (?, ?, ?, ?)",
            (
                "Password reset runbook",
                "Reset password tokens expire after 15 minutes and are invalidated after use.",
                '{"owner":"auth-team","severity":"high"}',
                "Bearer sk-secret-token-value-1234567890",
            ),
        )
        con.execute(
            "INSERT INTO notes (title, body, meta, secret) VALUES (?, ?, ?, ?)",
            (
                "Session continuity",
                "Session lineage should resolve parent child chains before summarization.",
                '{"kind":"memory","priority":2}',
                None,
            ),
        )
        con.commit()
    finally:
        con.close()
    return path


def test_sqlite_header_detection(tmp_path):
    db_path = _make_sqlite_db(tmp_path / "sample.db")
    fake_path = tmp_path / "fake.db"
    fake_path.write_text("not really sqlite", encoding="utf-8")

    assert lss_extract.is_sqlite_file(str(db_path)) is True
    assert lss_extract.is_sqlite_file(str(fake_path)) is False


def test_sqlite_discovery_and_indexing(tmp_path):
    db_path = _make_sqlite_db(tmp_path / "sample.sqlite3")

    all_paths, new_paths, already = discover_files(db_path)
    assert all_paths == [db_path]
    assert new_paths == [db_path]
    assert already == 0

    ingest_many([db_path])

    con = sqlite3.connect(get_db_path())
    try:
        rows = con.execute(
            "SELECT id, text FROM fts WHERE file_path = ? ORDER BY id",
            (str(db_path.resolve()),),
        ).fetchall()
    finally:
        con.close()

    assert len(rows) == 2
    assert any("Table: notes" in row[1] for row in rows)
    assert any("Row: id=1" in row[1] for row in rows)
    assert any("owner: auth-team" in row[1] for row in rows)
    assert any("[REDACTED]" in row[1] for row in rows)


def test_sqlite_search_returns_metadata(tmp_path, monkeypatch):
    monkeypatch.delenv("OPENAI_API_KEY", raising=False)
    monkeypatch.setattr(semantic_search_module, "EMBED_PROVIDER", "local")
    monkeypatch.setattr(semantic_search_module, "_local_embed", lambda texts: None)
    db_path = _make_sqlite_db(tmp_path / "sample.db")
    ingest_many([db_path])

    results = semantic_search(
        str(db_path), ["reset password token expire 15 minutes"], db_path=get_db_path()
    )

    assert len(results) == 1
    assert results[0]
    top = results[0][0]
    assert top["file_path"] == str(db_path.resolve())
    assert top["source_type"] == "sqlite"
    assert top["table"] == "notes"
    assert "id=1" in top["row_id"]
    assert "expire after 15 minutes" in top["snippet"].lower()


def test_sqlite_cli_search_works_without_embeddings(tmp_path, monkeypatch):
    monkeypatch.delenv("OPENAI_API_KEY", raising=False)
    monkeypatch.setattr(semantic_search_module, "EMBED_PROVIDER", "local")
    monkeypatch.setattr(semantic_search_module, "_local_embed", lambda texts: None)
    db_path = _make_sqlite_db(tmp_path / "sample.db")

    old_stdout = sys.stdout
    sys.stdout = captured = StringIO()
    try:
        rc = lss_main(["search", "session lineage", "-p", str(db_path), "--json"])
    finally:
        sys.stdout = old_stdout

    assert rc == 0
    payload = __import__("json").loads(captured.getvalue())
    assert payload[0]["hits"][0]["source_type"] == "sqlite"
    assert payload[0]["hits"][0]["table"] == "notes"


def test_local_opencode_db_search_e2e(monkeypatch):
    monkeypatch.delenv("OPENAI_API_KEY", raising=False)
    monkeypatch.setattr(semantic_search_module, "EMBED_PROVIDER", "local")
    monkeypatch.setattr(semantic_search_module, "_local_embed", lambda texts: None)
    db_path = Path.home() / ".local" / "share" / "opencode" / "opencode.db"
    if not db_path.exists() or os.environ.get("RUN_LOCAL_OPENCODE_DB_E2E") != "1":
        return

    ingest_many([db_path])
    results = semantic_search(
        str(db_path), ["Hermes Agent vs Kortix memory system comparison"], db_path=get_db_path()
    )

    assert len(results) == 1
    assert results[0]
    assert any(hit.get("table") in {"session", "message", "part"} for hit in results[0])
