"""Tests for the tool-use fixes from the 2026-05-08 journal session."""

from types import SimpleNamespace
from unittest.mock import AsyncMock, patch

import pytest


def _project(pid: int, title: str, description: str = "", auto_summary: str = ""):
    return SimpleNamespace(
        id=pid, title=title, description=description, auto_summary=auto_summary,
    )


@pytest.mark.asyncio
async def test_search_projects_tool_returns_success_true():
    """The dispatcher sets status from result['success']; without this key
    the call gets labeled 'error' even when data is returned."""
    from fabledassistant.services.tools import projects as projects_tool

    fake_projects = [_project(5, "Famous-Supply Work topics", "AT&T fiber circuit")]

    # `list_projects` is imported locally inside search_projects_tool, so we
    # patch the source module rather than the consumer.
    with patch("fabledassistant.services.projects.list_projects", new=AsyncMock(return_value=fake_projects)):
        result = await projects_tool.search_projects_tool(
            user_id=1, arguments={"query": "famous supply"},
        )

    assert result["success"] is True
    assert result["type"] == "projects_list"
    assert len(result["data"]["projects"]) == 1


def test_strip_type_nouns_removes_task_word():
    from fabledassistant.services.notes import _strip_type_nouns
    assert _strip_type_nouns("sebring secondary task") == ["sebring", "secondary"]


def test_strip_type_nouns_removes_all_variants():
    from fabledassistant.services.notes import _strip_type_nouns
    assert _strip_type_nouns("project notes task") == []


def test_strip_type_nouns_case_insensitive():
    from fabledassistant.services.notes import _strip_type_nouns
    assert _strip_type_nouns("Sebring Task NOTES") == ["Sebring"]


def test_strip_type_nouns_preserves_real_content_words():
    from fabledassistant.services.notes import _strip_type_nouns
    assert _strip_type_nouns("circuit configuration") == ["circuit", "configuration"]


def test_strip_type_nouns_handles_empty_string():
    from fabledassistant.services.notes import _strip_type_nouns
    assert _strip_type_nouns("") == []
    assert _strip_type_nouns("   ") == []


def test_score_project_match_exact_title_returns_1():
    from fabledassistant.services.tools._helpers import score_project_match
    p = _project(5, "Famous-Supply Work topics")
    assert score_project_match("Famous-Supply Work topics", p) == 1.0


def test_score_project_match_colloquial_substring_at_least_85():
    """'famous supply' is a substring of normalized 'famous supply work topics'
    after stripping the hyphen. Substring match returns 0.85."""
    from fabledassistant.services.tools._helpers import score_project_match
    p = _project(5, "Famous-Supply Work topics", auto_summary="AT&T fiber circuit")
    score = score_project_match("famous supply project", p)
    assert score >= 0.85, f"expected substring tier (>=0.85), got {score}"


def test_score_project_match_query_in_summary_returns_70():
    from fabledassistant.services.tools._helpers import score_project_match
    p = _project(12, "Minstrel", auto_summary="self-hosted music server")
    assert score_project_match("music server", p) == 0.70


def test_score_project_match_unrelated_returns_low():
    from fabledassistant.services.tools._helpers import score_project_match
    p = _project(12, "Minstrel", auto_summary="self-hosted music server")
    assert score_project_match("garden renovation", p) < 0.5


def test_score_project_match_empty_query_returns_zero():
    from fabledassistant.services.tools._helpers import score_project_match
    p = _project(5, "Famous-Supply Work topics")
    assert score_project_match("", p) == 0.0


@pytest.mark.asyncio
async def test_search_projects_tool_ranks_substring_match_above_others():
    """Substring hits (score 0.85) must rank above SequenceMatcher misses
    for unrelated projects."""
    from fabledassistant.services.tools import projects as projects_tool

    fake_projects = [
        _project(12, "Minstrel", auto_summary="self-hosted music server"),
        _project(5, "Famous-Supply Work topics", auto_summary="AT&T fiber circuit"),
        _project(13, "ImageRepo", auto_summary="self-hosted Flask app"),
    ]

    with patch("fabledassistant.services.projects.list_projects", new=AsyncMock(return_value=fake_projects)):
        result = await projects_tool.search_projects_tool(
            user_id=1, arguments={"query": "famous supply project"},
        )

    top = result["data"]["projects"][0]
    assert top["id"] == 5, f"expected Famous-Supply to rank first, got {top}"
    assert top["score"] >= 0.85


@pytest.mark.asyncio
async def test_resolve_project_finds_colloquial_match(monkeypatch):
    """resolve_project must surface 'Famous-Supply Work topics' when the
    user passes 'famous supply project' — substring match via the shared
    score helper, score 0.85 ≥ 0.55 threshold."""
    from fabledassistant.services.tools import _helpers

    fake_projects = [
        _project(12, "Minstrel", auto_summary="self-hosted music server"),
        _project(5, "Famous-Supply Work topics", auto_summary="AT&T fiber circuit"),
    ]

    async def fake_get_project_by_title(uid, name):
        return None  # force the scored path

    async def fake_list_projects(uid):
        return fake_projects

    monkeypatch.setattr(
        "fabledassistant.services.projects.get_project_by_title",
        fake_get_project_by_title,
    )
    monkeypatch.setattr(
        "fabledassistant.services.projects.list_projects",
        fake_list_projects,
    )

    result = await _helpers.resolve_project(user_id=1, project_name="famous supply project")
    assert result is not None
    assert result.id == 5
