"""
Tests for the MCP server startup health check.
"""

import json
import sys
from datetime import datetime, timedelta, timezone
from unittest.mock import MagicMock, patch

import pytest

from novyx_mcp.local_backend import LocalBackend


# =========================================================================
# Fixtures
# =========================================================================


@pytest.fixture
def backend(tmp_path):
    """Create a LocalBackend with an isolated temp database."""
    return LocalBackend(data_dir=str(tmp_path))


# =========================================================================
# _compute_health_score
# =========================================================================


class TestComputeHealthScore:
    def test_empty_memory_returns_100(self):
        from novyx_mcp.server import _compute_health_score

        assert _compute_health_score(total=0, stale=0, conflicts=0) == 100

    def test_no_issues_returns_100(self):
        from novyx_mcp.server import _compute_health_score

        assert _compute_health_score(total=50, stale=0, conflicts=0) == 100

    def test_all_stale_loses_30(self):
        from novyx_mcp.server import _compute_health_score

        # 100% stale ratio -> 30 point penalty (capped)
        assert _compute_health_score(total=10, stale=10, conflicts=0) == 70

    def test_half_stale(self):
        from novyx_mcp.server import _compute_health_score

        # 50% stale -> 50 * 1 = 50, capped at 30 penalty
        assert _compute_health_score(total=100, stale=50, conflicts=0) == 70

    def test_small_stale_ratio(self):
        from novyx_mcp.server import _compute_health_score

        # 3/100 = 3% -> 3 point penalty
        assert _compute_health_score(total=100, stale=3, conflicts=0) == 97

    def test_conflicts_penalty(self):
        from novyx_mcp.server import _compute_health_score

        # 2 conflicts -> 10 point penalty
        assert _compute_health_score(total=50, stale=0, conflicts=2) == 90

    def test_conflicts_capped_at_20(self):
        from novyx_mcp.server import _compute_health_score

        # 10 conflicts -> 50, capped at 20
        assert _compute_health_score(total=50, stale=0, conflicts=10) == 80

    def test_stale_and_conflicts_combined(self):
        from novyx_mcp.server import _compute_health_score

        # 100% stale (30 penalty) + 2 conflicts (10 penalty) = 60
        assert _compute_health_score(total=10, stale=10, conflicts=2) == 60

    def test_score_never_below_zero(self):
        from novyx_mcp.server import _compute_health_score

        assert _compute_health_score(total=1, stale=1, conflicts=100) >= 0


# =========================================================================
# _count_stale_memories (local backend)
# =========================================================================


class TestCountStaleMemories:
    def test_no_stale_when_recent(self, backend):
        from novyx_mcp.server import _count_stale_memories

        backend.remember(observation="Fresh memory", importance=5)
        cutoff = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()
        assert _count_stale_memories(backend, cutoff) == 0

    def test_stale_when_old(self, backend):
        from novyx_mcp.server import _count_stale_memories

        # Insert a memory, then backdate it
        result = backend.remember(observation="Old memory", importance=5)
        old_date = (datetime.now(timezone.utc) - timedelta(days=60)).isoformat()
        backend._conn.execute(
            "UPDATE memories SET created_at = ?, updated_at = ? WHERE uuid = ?",
            (old_date, old_date, result["uuid"]),
        )
        backend._conn.commit()

        cutoff = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()
        assert _count_stale_memories(backend, cutoff) == 1

    def test_mixed_stale_and_fresh(self, backend):
        from novyx_mcp.server import _count_stale_memories

        # Fresh
        backend.remember(observation="Fresh memory", importance=5)
        # Old
        result = backend.remember(observation="Old memory", importance=5)
        old_date = (datetime.now(timezone.utc) - timedelta(days=60)).isoformat()
        backend._conn.execute(
            "UPDATE memories SET created_at = ?, updated_at = ? WHERE uuid = ?",
            (old_date, old_date, result["uuid"]),
        )
        backend._conn.commit()

        cutoff = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()
        assert _count_stale_memories(backend, cutoff) == 1


# =========================================================================
# startup_health_check (integration)
# =========================================================================


class TestStartupHealthCheck:
    def test_no_memories(self, backend, capsys):
        from novyx_mcp.server import startup_health_check

        with patch("novyx_mcp.server._get_backend", return_value=backend):
            with patch("novyx_mcp.server._backend_instance", backend):
                msg = startup_health_check()

        assert msg == "Novyx: ready (no memories yet)"
        captured = capsys.readouterr()
        assert "Novyx: ready (no memories yet)" in captured.err

    def test_healthy_memories(self, backend, capsys):
        from novyx_mcp.server import startup_health_check

        backend.remember(observation="Test memory 1", importance=5)
        backend.remember(observation="Test memory 2", importance=7)

        with patch("novyx_mcp.server._get_backend", return_value=backend):
            with patch("novyx_mcp.server._backend_instance", backend):
                msg = startup_health_check()

        assert "2 memories" in msg
        assert "health 100/100" in msg
        captured = capsys.readouterr()
        assert "Novyx:" in captured.err

    def test_with_stale_memories(self, backend, capsys):
        from novyx_mcp.server import startup_health_check

        # Create memories
        fresh = backend.remember(observation="Fresh", importance=5)
        old = backend.remember(observation="Old", importance=5)

        # Backdate one
        old_date = (datetime.now(timezone.utc) - timedelta(days=60)).isoformat()
        backend._conn.execute(
            "UPDATE memories SET created_at = ?, updated_at = ? WHERE uuid = ?",
            (old_date, old_date, old["uuid"]),
        )
        backend._conn.commit()

        with patch("novyx_mcp.server._get_backend", return_value=backend):
            with patch("novyx_mcp.server._backend_instance", backend):
                msg = startup_health_check()

        assert "2 memories" in msg
        assert "1 stale" in msg
        assert "health" in msg

    def test_with_low_confidence_conflicts(self, backend, capsys):
        from novyx_mcp.server import startup_health_check

        backend.remember(observation="Good memory", importance=5)
        # Insert a low-confidence memory directly
        result = backend.remember(observation="Conflicted", importance=5)
        backend._conn.execute(
            "UPDATE memories SET confidence = 0.3 WHERE uuid = ?",
            (result["uuid"],),
        )
        backend._conn.commit()

        with patch("novyx_mcp.server._get_backend", return_value=backend):
            with patch("novyx_mcp.server._backend_instance", backend):
                msg = startup_health_check()

        assert "2 memories" in msg
        assert "1 conflicts" in msg

    def test_backend_failure_graceful(self, capsys):
        from novyx_mcp.server import startup_health_check

        mock_backend = MagicMock()
        mock_backend.stats.side_effect = Exception("Connection refused")

        with patch("novyx_mcp.server._get_backend", return_value=mock_backend):
            with patch("novyx_mcp.server._backend_instance", mock_backend):
                msg = startup_health_check()

        assert msg == "Novyx: ready (no memories yet)"
        captured = capsys.readouterr()
        assert "Novyx: ready (no memories yet)" in captured.err

    def test_memory_health_tool_no_memories(self, backend):
        from novyx_mcp.server import memory_health

        with patch("novyx_mcp.server._get_backend", return_value=backend):
            with patch("novyx_mcp.server._backend_instance", backend):
                result = json.loads(memory_health())

        assert result["score"] == 100
        assert result["total"] == 0

    def test_memory_health_tool_with_memories(self, backend):
        from novyx_mcp.server import memory_health

        backend.remember(observation="Test 1", importance=5)
        backend.remember(observation="Test 2", importance=8)

        with patch("novyx_mcp.server._get_backend", return_value=backend):
            with patch("novyx_mcp.server._backend_instance", backend):
                result = json.loads(memory_health())

        assert result["score"] == 100
        assert result["total"] == 2
        assert result["stale"] == 0
        assert "breakdown" in result

    def test_cloud_backend_stats(self, capsys):
        from novyx_mcp.server import startup_health_check

        mock_backend = MagicMock()
        mock_backend.stats.return_value = {"total_memories": 142}
        # No _conn attribute -> cloud path
        del mock_backend._conn
        mock_backend.memories.return_value = [
            {"uuid": "m1", "updated_at": "2026-03-10T00:00:00Z", "confidence": 1.0},
            {"uuid": "m2", "updated_at": "2026-01-01T00:00:00Z", "confidence": 1.0},
        ]

        with patch("novyx_mcp.server._get_backend", return_value=mock_backend):
            with patch("novyx_mcp.server._backend_instance", mock_backend):
                msg = startup_health_check()

        assert "142 memories" in msg
        assert "health" in msg
