"""Tests for knowledge skills — save, query, update handlers."""

import pytest

from fliiq.runtime.knowledge.store import KnowledgeStore


@pytest.fixture
def _patch_store(tmp_path, monkeypatch):
    """Patch KnowledgeStore.from_fliiq_dir to use tmp_path."""
    store = KnowledgeStore(tmp_path / "knowledge")
    store.ensure_dirs()
    monkeypatch.setattr(
        "fliiq.runtime.knowledge.store.KnowledgeStore.from_fliiq_dir",
        classmethod(lambda cls: store),
    )
    return store


@pytest.mark.asyncio
class TestKnowledgeSave:
    async def test_save_basic(self, _patch_store):
        from fliiq.data.skills.core.knowledge_save.main import handler

        result = await handler({
            "type": "decision",
            "source": "human_in_loop",
            "context": "SMS provider",
            "decision": "Use Twilio",
            "rationale": "Best pricing",
        })
        assert result["id"].startswith("kr_")
        assert "saved" in result["result"]

    async def test_save_with_domain_and_tags(self, _patch_store):
        from fliiq.data.skills.core.knowledge_save.main import handler

        result = await handler({
            "type": "heuristic",
            "source": "agent_decision",
            "context": "API design",
            "decision": "Always version endpoints",
            "rationale": "Prevents breaking changes",
            "domain": "coding",
            "tags": ["api", "best-practice"],
            "confidence": 0.9,
        })
        assert result["id"].startswith("kr_")

        # Verify it's in the store
        record = _patch_store.load(result["id"])
        assert record.domain == "coding"
        assert record.tags == ["api", "best-practice"]
        assert record.confidence == 0.9

    async def test_save_with_anchor(self, _patch_store, tmp_path):
        from fliiq.data.skills.core.knowledge_save.main import handler

        # Create a file to anchor to
        code_file = tmp_path / "src" / "main.py"
        code_file.parent.mkdir(parents=True)
        code_file.write_text("def foo():\n    return 42\n")

        result = await handler({
            "type": "decision",
            "source": "human_in_loop",
            "context": "foo() implementation",
            "decision": "Return 42",
            "rationale": "The answer to everything",
            "anchor_file": str(code_file),
            "anchor_lines": "1-2",
        })

        record = _patch_store.load(result["id"])
        assert record.anchor is not None
        assert record.anchor.file == str(code_file)
        assert record.anchor.line_start == 1
        assert record.anchor.line_end == 2
        assert record.anchor.content_hash is not None

    async def test_save_anchor_missing_file(self, _patch_store):
        from fliiq.data.skills.core.knowledge_save.main import handler

        result = await handler({
            "type": "decision",
            "source": "human_in_loop",
            "context": "test",
            "decision": "test",
            "rationale": "test",
            "anchor_file": "/nonexistent/file.py",
            "anchor_lines": "1-5",
        })
        # Should still save, just without content_hash
        record = _patch_store.load(result["id"])
        assert record.anchor.content_hash is None


@pytest.mark.asyncio
class TestKnowledgeQuery:
    async def test_query_by_keyword(self, _patch_store):
        from fliiq.data.skills.core.knowledge_query.main import handler
        from fliiq.data.skills.core.knowledge_save.main import handler as save

        await save({
            "type": "decision", "source": "human_in_loop",
            "context": "SMS", "decision": "Use Twilio for SMS",
            "rationale": "Best pricing and reliability",
        })

        result = await handler({"query": "Twilio SMS"})
        assert result["count"] >= 1
        assert "Twilio" in result["records"]

    async def test_query_by_domain(self, _patch_store):
        from fliiq.data.skills.core.knowledge_query.main import handler
        from fliiq.data.skills.core.knowledge_save.main import handler as save

        await save({
            "type": "decision", "source": "human_in_loop",
            "context": "infra", "decision": "Use Twilio",
            "rationale": "pricing", "domain": "infrastructure",
        })
        await save({
            "type": "decision", "source": "human_in_loop",
            "context": "code", "decision": "Use Python",
            "rationale": "readability", "domain": "coding",
        })

        result = await handler({"domain": "infrastructure"})
        assert result["count"] == 1
        assert "Twilio" in result["records"]

    async def test_query_empty_store(self, _patch_store):
        from fliiq.data.skills.core.knowledge_query.main import handler

        result = await handler({})
        assert result["count"] == 0
        assert "empty" in result["records"].lower()

    async def test_query_no_match(self, _patch_store):
        from fliiq.data.skills.core.knowledge_query.main import handler
        from fliiq.data.skills.core.knowledge_save.main import handler as save

        await save({
            "type": "decision", "source": "human_in_loop",
            "context": "test", "decision": "Use Python",
            "rationale": "readability",
        })

        result = await handler({"query": "xyznonexistent"})
        assert result["count"] == 0


@pytest.mark.asyncio
class TestKnowledgeUpdate:
    async def test_update_confidence(self, _patch_store):
        from fliiq.data.skills.core.knowledge_save.main import handler as save
        from fliiq.data.skills.core.knowledge_update.main import handler

        saved = await save({
            "type": "decision", "source": "human_in_loop",
            "context": "test", "decision": "test", "rationale": "test",
        })

        result = await handler({"id": saved["id"], "confidence": 0.95})
        assert result["updated"] is True

        record = _patch_store.load(saved["id"])
        assert record.confidence == 0.95

    async def test_expire_record(self, _patch_store):
        from fliiq.data.skills.core.knowledge_save.main import handler as save
        from fliiq.data.skills.core.knowledge_update.main import handler

        saved = await save({
            "type": "decision", "source": "human_in_loop",
            "context": "test", "decision": "test", "rationale": "test",
        })

        result = await handler({"id": saved["id"], "expired": True})
        assert result["updated"] is True
        assert "expired" in result["result"]

    async def test_update_nonexistent(self, _patch_store):
        from fliiq.data.skills.core.knowledge_update.main import handler

        result = await handler({"id": "kr_nonexistent", "confidence": 0.5})
        assert result["updated"] is False

    async def test_update_no_fields(self, _patch_store):
        from fliiq.data.skills.core.knowledge_update.main import handler

        result = await handler({"id": "kr_something"})
        assert result["updated"] is False
        assert "No fields" in result["result"]
