# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# Licensed under the MIT License

"""Tests for Sprint 6 — Proactive Alerts + Agent Awareness."""

from unittest.mock import MagicMock, patch

import pytest

from familiar.core.credential_health import (
    CredentialStatus,
    clear_attention,
    get_attention_needed,
)
from familiar.core.credential_health import (
    _reset_for_testing as reset_health,
)


@pytest.fixture(autouse=True)
def clean_state(tmp_path, monkeypatch):
    """Reset credential health state and redirect status file."""
    reset_health()
    status_file = tmp_path / "credential_status.json"
    monkeypatch.setattr("familiar.core.credential_health._STATUS_FILE", status_file)
    # is_configured() gates get_attention_needed() — patch so test service keys pass through
    monkeypatch.setattr("familiar.core.credential_registry.is_configured", lambda key: True)
    yield
    reset_health()


# ── PromptBuilder credential warnings ────────────────────────────────────


class TestPromptBuilderCredentialWarnings:
    def _make_builder(self):
        from familiar.core.prompt_builder import PromptBuilder

        agent = MagicMock()
        agent.config.agent.name = "Test"
        agent.config.agent.persona = "helpful"
        agent.config.agent.skills_enabled = False
        agent.memory = None
        agent.provider.name = "test"
        return PromptBuilder(agent)

    def test_no_warnings_when_clean(self):
        builder = self._make_builder()
        result = builder._get_credential_warnings()
        assert result == ""

    def test_warnings_when_attention_needed(self):
        from familiar.core.credential_health import _set_cached

        _set_cached(
            CredentialStatus(
                service_key="github",
                configured=True,
                valid=False,
                attention_needed=True,
                last_error="HTTP 401: Bad credentials",
            )
        )

        builder = self._make_builder()
        result = builder._get_credential_warnings()
        assert "<credential_warnings>" in result
        assert "github" in result
        assert "401" in result
        assert "Settings > Credentials" in result

    def test_multiple_warnings(self):
        from familiar.core.credential_health import _set_cached

        _set_cached(
            CredentialStatus(
                service_key="github",
                configured=True,
                valid=False,
                attention_needed=True,
                last_error="Token expired",
            )
        )
        _set_cached(
            CredentialStatus(
                service_key="slack",
                configured=True,
                valid=False,
                attention_needed=True,
                last_error="Invalid token",
            )
        )

        builder = self._make_builder()
        result = builder._get_credential_warnings()
        assert "github" in result
        assert "slack" in result

    def test_no_warning_for_valid_credentials(self):
        from familiar.core.credential_health import _set_cached

        _set_cached(
            CredentialStatus(
                service_key="github",
                configured=True,
                valid=True,
                attention_needed=False,
            )
        )

        builder = self._make_builder()
        result = builder._get_credential_warnings()
        assert result == ""


# ── API: /api/credentials/attention ───────────────────────────────────────


@pytest.fixture
def dashboard_client(monkeypatch):
    """Create a Flask test client with auth configured."""
    monkeypatch.setenv("FAMILIAR_DASHBOARD_KEY", "test-key-for-auth")
    from familiar.dashboard.app import app

    app.config["TESTING"] = True
    with app.test_client() as client:
        yield client


def _auth_headers(post=False):
    h = {"X-API-Key": "test-key-for-auth"}
    if post:
        h["Origin"] = "http://localhost:5000"
    return h


class TestCredentialAttentionEndpoint:
    def test_empty_attention(self, dashboard_client):
        resp = dashboard_client.get("/api/credentials/attention", headers=_auth_headers(post=False))
        assert resp.status_code == 200
        data = resp.get_json()
        assert data["count"] == 0
        assert data["services"] == []

    def test_with_attention_needed(self, dashboard_client):
        from familiar.core.credential_health import _set_cached

        _set_cached(
            CredentialStatus(
                service_key="github",
                configured=True,
                valid=False,
                attention_needed=True,
                last_error="Token expired",
            )
        )

        resp = dashboard_client.get("/api/credentials/attention", headers=_auth_headers(post=False))
        assert resp.status_code == 200
        data = resp.get_json()
        assert data["count"] == 1
        assert data["services"][0]["service_key"] == "github"
        assert "expired" in data["services"][0]["last_error"].lower()


# ── API: /api/credentials/dismiss ─────────────────────────────────────────


class TestCredentialDismissEndpoint:
    def test_dismiss_clears_attention(self, dashboard_client):
        from familiar.core.credential_health import _set_cached

        _set_cached(
            CredentialStatus(
                service_key="slack",
                configured=True,
                valid=False,
                attention_needed=True,
                last_error="Invalid token",
            )
        )

        resp = dashboard_client.post(
            "/api/credentials/dismiss/slack",
            headers=_auth_headers(post=True),
        )
        assert resp.status_code == 200
        data = resp.get_json()
        assert data["status"] == "dismissed"

        # Verify attention is cleared
        resp2 = dashboard_client.get(
            "/api/credentials/attention", headers=_auth_headers(post=False)
        )
        assert resp2.get_json()["count"] == 0

    def test_dismiss_unknown_noop(self, dashboard_client):
        """Dismissing a service that isn't in the cache should not error."""
        resp = dashboard_client.post(
            "/api/credentials/dismiss/nonexistent",
            headers=_auth_headers(post=True),
        )
        assert resp.status_code == 200


# ── clear_attention + get_attention_needed ────────────────────────────────


class TestAttentionHelpers:
    def test_get_attention_empty(self):
        assert get_attention_needed() == []

    def test_get_attention_filters_correctly(self):
        from familiar.core.credential_health import _set_cached

        _set_cached(
            CredentialStatus(
                service_key="github",
                configured=True,
                valid=False,
                attention_needed=True,
                last_error="bad token",
            )
        )
        _set_cached(
            CredentialStatus(
                service_key="slack",
                configured=True,
                valid=True,
                attention_needed=False,
            )
        )

        result = get_attention_needed()
        assert len(result) == 1
        assert result[0].service_key == "github"

    def test_clear_attention_works(self):
        from familiar.core.credential_health import _set_cached

        _set_cached(
            CredentialStatus(
                service_key="github",
                configured=True,
                valid=False,
                attention_needed=True,
                last_error="expired",
            )
        )
        assert len(get_attention_needed()) == 1

        clear_attention("github")
        assert len(get_attention_needed()) == 0
