# #region Test.Api.DashboardHelpers [C:3] [TYPE Module] [SEMANTICS test,dashboard,helpers]
# @BRIEF Unit tests for dashboard helper functions.
# @RELATION BINDS_TO -> [DashboardHelpers]
# @TEST_EDGE: empty_ref -> 404
# @TEST_EDGE: slug_resolution -> int | None
# @TEST_EDGE: numeric_ref_fallback -> int

import sys
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

_src = str(Path(__file__).resolve().parent.parent.parent / "src")
if _src not in sys.path:
    sys.path.insert(0, _src)


# ── Deprecated sync functions ──

class TestDeprecatedSyncFunctions:
    """_find_dashboard_id_by_slug and _resolve_dashboard_id_from_ref (sync) raise RuntimeError."""

    def test_find_dashboard_id_by_slug_deprecated(self):
        from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug
        with pytest.raises(RuntimeError, match="deprecated"):
            _find_dashboard_id_by_slug(MagicMock(), "test-slug")

    def test_resolve_dashboard_id_from_ref_multiple_defs(self):
        """The module has two defs of _resolve_dashboard_id_from_ref. The second wins.
        Both are Tombstone and raise RuntimeError."""
        from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref

        client = MagicMock()
        client.get_dashboards_page.return_value = (0, [])

        with pytest.raises(RuntimeError, match="deprecated"):
            _resolve_dashboard_id_from_ref("unknown-ref", client)


# ── _find_dashboard_id_by_slug_async ──

class TestFindDashboardIdBySlugAsync:
    """_find_dashboard_id_by_slug_async"""

    @pytest.mark.asyncio
    async def test_found_by_slug(self):
        client = AsyncMock()
        client.get_dashboards_page.return_value = (1, [{"id": 42}])

        from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug_async
        result = await _find_dashboard_id_by_slug_async(client, "my-slug")
        assert result == 42

    @pytest.mark.asyncio
    async def test_not_found(self):
        client = AsyncMock()
        client.get_dashboards_page.return_value = (0, [])

        from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug_async
        result = await _find_dashboard_id_by_slug_async(client, "ghost-slug")
        assert result is None

    @pytest.mark.asyncio
    async def test_exception_then_second_query(self):
        client = AsyncMock()
        # First query raises, second succeeds
        client.get_dashboards_page.side_effect = [
            Exception("First query failed"),
            (1, [{"id": 99}]),
        ]

        from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug_async
        result = await _find_dashboard_id_by_slug_async(client, "retry-slug")
        assert result == 99

    @pytest.mark.asyncio
    async def test_all_queries_fail(self):
        client = AsyncMock()
        client.get_dashboards_page.side_effect = Exception("All failed")

        from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug_async
        result = await _find_dashboard_id_by_slug_async(client, "fail-slug")
        assert result is None


# ── _resolve_dashboard_id_from_ref_async ──

class TestResolveDashboardIdFromRefAsync:
    """_resolve_dashboard_id_from_ref_async"""

    @pytest.mark.asyncio
    async def test_numeric_ref(self):
        from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
        result = await _resolve_dashboard_id_from_ref_async("123", AsyncMock())
        assert result == 123

    @pytest.mark.asyncio
    async def test_slug_ref(self):
        from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
        client = AsyncMock()
        client.get_dashboards_page.return_value = (1, [{"id": 42}])
        result = await _resolve_dashboard_id_from_ref_async("my-dash", client)
        assert result == 42

    @pytest.mark.asyncio
    async def test_empty_ref(self):
        from fastapi import HTTPException
        from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
        with pytest.raises(HTTPException) as exc:
            await _resolve_dashboard_id_from_ref_async("", AsyncMock())
        assert exc.value.status_code == 404

    @pytest.mark.asyncio
    async def test_slug_not_found(self):
        from fastapi import HTTPException
        from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
        client = AsyncMock()
        client.get_dashboards_page.return_value = (0, [])
        with pytest.raises(HTTPException) as exc:
            await _resolve_dashboard_id_from_ref_async("ghost", client)
        assert exc.value.status_code == 404

    @pytest.mark.asyncio
    async def test_none_ref(self):
        from fastapi import HTTPException
        from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
        with pytest.raises(HTTPException) as exc:
            await _resolve_dashboard_id_from_ref_async(None, AsyncMock())  # type: ignore
        assert exc.value.status_code == 404


# ── _normalize_filter_values ──

class TestNormalizeFilterValues:
    """_normalize_filter_values"""

    def test_normalize_returns_lowercase(self):
        from src.api.routes.dashboards._helpers import _normalize_filter_values
        assert _normalize_filter_values(["HELLO", "World"]) == ["hello", "world"]

    def test_normalize_empty_list(self):
        from src.api.routes.dashboards._helpers import _normalize_filter_values
        assert _normalize_filter_values([]) == []

    def test_normalize_none(self):
        from src.api.routes.dashboards._helpers import _normalize_filter_values
        assert _normalize_filter_values(None) == []

    def test_normalize_removes_empty_strings(self):
        from src.api.routes.dashboards._helpers import _normalize_filter_values
        assert _normalize_filter_values(["a", "", "  ", "b"]) == ["a", "b"]

    def test_normalize_strips_whitespace(self):
        from src.api.routes.dashboards._helpers import _normalize_filter_values
        assert _normalize_filter_values(["  Foo Bar  "]) == ["foo bar"]


# ── _dashboard_git_filter_value ──

class TestDashboardGitFilterValue:
    """_dashboard_git_filter_value"""

    def test_no_repo(self):
        from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
        assert _dashboard_git_filter_value({"git_status": {"has_repo": False}}) == "no_repo"

    def test_no_repo_status(self):
        from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
        assert _dashboard_git_filter_value({"git_status": {"sync_status": "NO_REPO"}}) == "no_repo"

    def test_diff(self):
        from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
        assert _dashboard_git_filter_value({"git_status": {"sync_status": "DIFF", "has_repo": True}}) == "diff"

    def test_ok(self):
        from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
        assert _dashboard_git_filter_value({"git_status": {"sync_status": "OK", "has_repo": True}}) == "ok"

    def test_error(self):
        from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
        assert _dashboard_git_filter_value({"git_status": {"sync_status": "ERROR", "has_repo": True}}) == "error"

    def test_missing_git_status(self):
        from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
        assert _dashboard_git_filter_value({}) == "pending"

    def test_none_git_status(self):
        from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
        assert _dashboard_git_filter_value({"git_status": None}) == "pending"
# #endregion Test.Api.DashboardHelpers
