"""
Tests für Alerts Agent Topics API.

Testet CRUD-Operationen für Topics über die REST-API.
"""
import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch, AsyncMock

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool

# WICHTIG: Models ZUERST importieren damit sie bei Base registriert werden
from alerts_agent.db.models import (
    AlertTopicDB, AlertItemDB, AlertRuleDB, AlertProfileDB,
)
# Dann Base importieren (hat jetzt die Models in metadata)
from classroom_engine.database import Base
from alerts_agent.db import get_db
from alerts_agent.api.topics import router


# Test-Client Setup
from fastapi import FastAPI

app = FastAPI()
app.include_router(router, prefix="/api/alerts")


@pytest.fixture(scope="function")
def db_engine():
    """Erstellt eine In-Memory SQLite-Engine mit Threading-Support."""
    # StaticPool stellt sicher, dass alle Connections die gleiche DB nutzen
    # check_same_thread=False erlaubt Cross-Thread-Zugriff (für TestClient)
    engine = create_engine(
        "sqlite:///:memory:",
        echo=False,
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    # Alle Tables erstellen
    Base.metadata.create_all(engine)
    yield engine
    engine.dispose()


@pytest.fixture(scope="function")
def db_session(db_engine):
    """Erstellt eine Session für Tests."""
    SessionLocal = sessionmaker(bind=db_engine)
    session = SessionLocal()
    yield session
    session.rollback()
    session.close()


@pytest.fixture(scope="function")
def client(db_session):
    """Erstellt einen Test-Client mit überschriebener DB-Dependency."""
    def override_get_db():
        try:
            yield db_session
        finally:
            db_session.rollback()

    app.dependency_overrides[get_db] = override_get_db
    with TestClient(app) as test_client:
        yield test_client
    app.dependency_overrides.clear()


# =============================================================================
# CREATE TESTS
# =============================================================================

class TestCreateTopic:
    """Tests für POST /api/alerts/topics"""

    def test_create_topic_minimal(self, client):
        """Test: Topic mit minimalen Daten erstellen."""
        response = client.post(
            "/api/alerts/topics",
            json={"name": "Test Topic"},
        )

        assert response.status_code == 201
        data = response.json()
        assert data["name"] == "Test Topic"
        assert data["is_active"] is True
        assert data["feed_type"] == "rss"
        assert data["fetch_interval_minutes"] == 60

    def test_create_topic_full(self, client):
        """Test: Topic mit allen Feldern erstellen."""
        response = client.post(
            "/api/alerts/topics",
            json={
                "name": "Vollständiges Topic",
                "description": "Eine Beschreibung",
                "feed_url": "https://example.com/feed.rss",
                "feed_type": "rss",
                "fetch_interval_minutes": 30,
                "is_active": False,
            },
        )

        assert response.status_code == 201
        data = response.json()
        assert data["name"] == "Vollständiges Topic"
        assert data["description"] == "Eine Beschreibung"
        assert data["feed_url"] == "https://example.com/feed.rss"
        assert data["fetch_interval_minutes"] == 30
        assert data["is_active"] is False

    def test_create_topic_empty_name_fails(self, client):
        """Test: Leerer Name führt zu Fehler."""
        response = client.post(
            "/api/alerts/topics",
            json={"name": ""},
        )

        assert response.status_code == 422  # Validation Error

    def test_create_topic_invalid_interval(self, client):
        """Test: Ungültiges Fetch-Intervall führt zu Fehler."""
        response = client.post(
            "/api/alerts/topics",
            json={
                "name": "Test",
                "fetch_interval_minutes": 1,  # < 5 ist ungültig
            },
        )

        assert response.status_code == 422


# =============================================================================
# READ TESTS
# =============================================================================

class TestReadTopics:
    """Tests für GET /api/alerts/topics"""

    def test_list_topics_empty(self, client):
        """Test: Leere Topic-Liste."""
        response = client.get("/api/alerts/topics")

        assert response.status_code == 200
        data = response.json()
        assert data["topics"] == []
        assert data["total"] == 0

    def test_list_topics(self, client):
        """Test: Topics auflisten."""
        # Erstelle Topics
        client.post("/api/alerts/topics", json={"name": "Topic 1"})
        client.post("/api/alerts/topics", json={"name": "Topic 2"})
        client.post("/api/alerts/topics", json={"name": "Topic 3"})

        response = client.get("/api/alerts/topics")

        assert response.status_code == 200
        data = response.json()
        assert data["total"] == 3
        assert len(data["topics"]) == 3

    def test_list_topics_filter_active(self, client):
        """Test: Nur aktive Topics auflisten."""
        # Erstelle aktives und inaktives Topic
        client.post("/api/alerts/topics", json={"name": "Aktiv", "is_active": True})
        client.post("/api/alerts/topics", json={"name": "Inaktiv", "is_active": False})

        response = client.get("/api/alerts/topics?is_active=true")

        assert response.status_code == 200
        data = response.json()
        assert data["total"] == 1
        assert data["topics"][0]["name"] == "Aktiv"

    def test_get_topic_by_id(self, client):
        """Test: Topic nach ID abrufen."""
        # Erstelle Topic
        create_response = client.post(
            "/api/alerts/topics",
            json={"name": "Find Me"},
        )
        topic_id = create_response.json()["id"]

        # Abrufen
        response = client.get(f"/api/alerts/topics/{topic_id}")

        assert response.status_code == 200
        assert response.json()["name"] == "Find Me"

    def test_get_topic_not_found(self, client):
        """Test: Topic nicht gefunden."""
        response = client.get("/api/alerts/topics/nonexistent-id")

        assert response.status_code == 404


# =============================================================================
# UPDATE TESTS
# =============================================================================

class TestUpdateTopic:
    """Tests für PUT /api/alerts/topics/{id}"""

    def test_update_topic_name(self, client):
        """Test: Topic-Namen aktualisieren."""
        # Erstelle Topic
        create_response = client.post(
            "/api/alerts/topics",
            json={"name": "Original"},
        )
        topic_id = create_response.json()["id"]

        # Update
        response = client.put(
            f"/api/alerts/topics/{topic_id}",
            json={"name": "Updated"},
        )

        assert response.status_code == 200
        assert response.json()["name"] == "Updated"

    def test_update_topic_partial(self, client):
        """Test: Partielles Update."""
        # Erstelle Topic
        create_response = client.post(
            "/api/alerts/topics",
            json={
                "name": "Original",
                "description": "Desc",
                "is_active": True,
            },
        )
        topic_id = create_response.json()["id"]

        # Nur is_active ändern
        response = client.put(
            f"/api/alerts/topics/{topic_id}",
            json={"is_active": False},
        )

        assert response.status_code == 200
        data = response.json()
        assert data["name"] == "Original"  # Unverändert
        assert data["description"] == "Desc"  # Unverändert
        assert data["is_active"] is False  # Geändert

    def test_update_topic_not_found(self, client):
        """Test: Update für nicht existierendes Topic."""
        response = client.put(
            "/api/alerts/topics/nonexistent-id",
            json={"name": "New Name"},
        )

        assert response.status_code == 404

    def test_update_topic_empty_fails(self, client):
        """Test: Update ohne Änderungen führt zu Fehler."""
        # Erstelle Topic
        create_response = client.post(
            "/api/alerts/topics",
            json={"name": "Test"},
        )
        topic_id = create_response.json()["id"]

        # Leeres Update
        response = client.put(
            f"/api/alerts/topics/{topic_id}",
            json={},
        )

        assert response.status_code == 400


# =============================================================================
# DELETE TESTS
# =============================================================================

class TestDeleteTopic:
    """Tests für DELETE /api/alerts/topics/{id}"""

    def test_delete_topic(self, client):
        """Test: Topic löschen."""
        # Erstelle Topic
        create_response = client.post(
            "/api/alerts/topics",
            json={"name": "To Delete"},
        )
        topic_id = create_response.json()["id"]

        # Löschen
        response = client.delete(f"/api/alerts/topics/{topic_id}")
        assert response.status_code == 204

        # Prüfen, dass es weg ist
        get_response = client.get(f"/api/alerts/topics/{topic_id}")
        assert get_response.status_code == 404

    def test_delete_topic_not_found(self, client):
        """Test: Löschen eines nicht existierenden Topics."""
        response = client.delete("/api/alerts/topics/nonexistent-id")

        assert response.status_code == 404


# =============================================================================
# STATS TESTS
# =============================================================================

class TestTopicStats:
    """Tests für GET /api/alerts/topics/{id}/stats"""

    def test_get_topic_stats(self, client):
        """Test: Topic-Statistiken abrufen."""
        # Erstelle Topic
        create_response = client.post(
            "/api/alerts/topics",
            json={"name": "Stats Test"},
        )
        topic_id = create_response.json()["id"]

        # Stats abrufen
        response = client.get(f"/api/alerts/topics/{topic_id}/stats")

        assert response.status_code == 200
        data = response.json()
        assert data["topic_id"] == topic_id
        assert data["name"] == "Stats Test"
        assert data["total_alerts"] == 0

    def test_get_stats_not_found(self, client):
        """Test: Stats für nicht existierendes Topic."""
        response = client.get("/api/alerts/topics/nonexistent-id/stats")

        assert response.status_code == 404


# =============================================================================
# ACTIVATION TESTS
# =============================================================================

class TestTopicActivation:
    """Tests für Topic-Aktivierung/-Deaktivierung"""

    def test_activate_topic(self, client):
        """Test: Topic aktivieren."""
        # Erstelle inaktives Topic
        create_response = client.post(
            "/api/alerts/topics",
            json={"name": "Inactive", "is_active": False},
        )
        topic_id = create_response.json()["id"]

        # Aktivieren
        response = client.post(f"/api/alerts/topics/{topic_id}/activate")

        assert response.status_code == 200
        assert response.json()["is_active"] is True

    def test_deactivate_topic(self, client):
        """Test: Topic deaktivieren."""
        # Erstelle aktives Topic
        create_response = client.post(
            "/api/alerts/topics",
            json={"name": "Active", "is_active": True},
        )
        topic_id = create_response.json()["id"]

        # Deaktivieren
        response = client.post(f"/api/alerts/topics/{topic_id}/deactivate")

        assert response.status_code == 200
        assert response.json()["is_active"] is False


# =============================================================================
# FETCH TESTS
# =============================================================================

class TestTopicFetch:
    """Tests für POST /api/alerts/topics/{id}/fetch"""

    def test_fetch_topic_no_url(self, client):
        """Test: Fetch ohne Feed-URL führt zu Fehler."""
        # Erstelle Topic ohne URL
        create_response = client.post(
            "/api/alerts/topics",
            json={"name": "No URL"},
        )
        topic_id = create_response.json()["id"]

        # Fetch versuchen
        response = client.post(f"/api/alerts/topics/{topic_id}/fetch")

        assert response.status_code == 400
        assert "Feed-URL" in response.json()["detail"]

    def test_fetch_topic_not_found(self, client):
        """Test: Fetch für nicht existierendes Topic."""
        response = client.post("/api/alerts/topics/nonexistent-id/fetch")

        assert response.status_code == 404

    @patch("alerts_agent.ingestion.rss_fetcher.fetch_and_store_feed", new_callable=AsyncMock)
    def test_fetch_topic_success(self, mock_fetch, client):
        """Test: Erfolgreiches Fetchen."""
        # Mock Setup - async function braucht AsyncMock
        mock_fetch.return_value = {
            "new_items": 5,
            "duplicates_skipped": 2,
        }

        # Erstelle Topic mit URL
        create_response = client.post(
            "/api/alerts/topics",
            json={
                "name": "With URL",
                "feed_url": "https://example.com/feed.rss",
            },
        )
        topic_id = create_response.json()["id"]

        # Fetch ausführen
        response = client.post(f"/api/alerts/topics/{topic_id}/fetch")

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert data["new_items"] == 5
        assert data["duplicates_skipped"] == 2
