"""
Communication Test API - Test Runner fuer Matrix & Jitsi Integration
Endpoint: /api/admin/communication-tests
"""

from fastapi import APIRouter
from pydantic import BaseModel
from typing import List, Optional, Literal
import httpx
import asyncio
import time
import os

router = APIRouter(prefix="/api/admin/communication-tests", tags=["Communication Tests"])

# ==============================================
# Models
# ==============================================

class TestResult(BaseModel):
    name: str
    description: str
    expected: str
    actual: str
    status: Literal["passed", "failed", "pending", "skipped"]
    duration_ms: float
    error_message: Optional[str] = None


class TestCategoryResult(BaseModel):
    category: str
    display_name: str
    description: str
    tests: List[TestResult]
    passed: int
    failed: int
    total: int


class FullTestResults(BaseModel):
    categories: List[TestCategoryResult]
    total_passed: int
    total_failed: int
    total_tests: int
    duration_ms: float


# ==============================================
# Configuration
# ==============================================

MATRIX_HOMESERVER = os.getenv("MATRIX_HOMESERVER", "http://matrix:8008")
JITSI_URL = os.getenv("JITSI_URL", "http://jitsi:8443")
CONSENT_SERVICE_URL = os.getenv("CONSENT_SERVICE_URL", "http://consent-service:8081")


# ==============================================
# Test Implementations
# ==============================================

async def test_matrix_health() -> TestResult:
    """Test Matrix Homeserver Health"""
    start = time.time()
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            # Matrix Federation API health check
            response = await client.get(f"{MATRIX_HOMESERVER}/_matrix/client/versions")
            duration = (time.time() - start) * 1000

            if response.status_code == 200:
                data = response.json()
                versions = data.get("versions", [])
                return TestResult(
                    name="Matrix Homeserver Health",
                    description="Prueft ob der Matrix Synapse Server erreichbar ist",
                    expected="HTTP 200 mit Client-Versionen",
                    actual=f"HTTP {response.status_code}, Versionen: {', '.join(versions[:3])}",
                    status="passed",
                    duration_ms=duration
                )
            else:
                return TestResult(
                    name="Matrix Homeserver Health",
                    description="Prueft ob der Matrix Synapse Server erreichbar ist",
                    expected="HTTP 200 mit Client-Versionen",
                    actual=f"HTTP {response.status_code}",
                    status="failed",
                    duration_ms=duration,
                    error_message=f"Unerwarteter Status: {response.status_code}"
                )
    except Exception as e:
        return TestResult(
            name="Matrix Homeserver Health",
            description="Prueft ob der Matrix Synapse Server erreichbar ist",
            expected="HTTP 200 mit Client-Versionen",
            actual=f"Fehler: {str(e)}",
            status="failed",
            duration_ms=(time.time() - start) * 1000,
            error_message=str(e)
        )


async def test_matrix_federation() -> TestResult:
    """Test Matrix Federation API"""
    start = time.time()
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.get(f"{MATRIX_HOMESERVER}/_matrix/federation/v1/version")
            duration = (time.time() - start) * 1000

            if response.status_code == 200:
                data = response.json()
                server_name = data.get("server", {}).get("name", "unknown")
                return TestResult(
                    name="Matrix Federation API",
                    description="Prueft ob die Federation-API fuer Server-zu-Server Kommunikation bereit ist",
                    expected="HTTP 200 mit Server-Info",
                    actual=f"Server: {server_name}",
                    status="passed",
                    duration_ms=duration
                )
            else:
                return TestResult(
                    name="Matrix Federation API",
                    description="Prueft ob die Federation-API fuer Server-zu-Server Kommunikation bereit ist",
                    expected="HTTP 200 mit Server-Info",
                    actual=f"HTTP {response.status_code}",
                    status="skipped",
                    duration_ms=duration,
                    error_message="Federation nicht aktiviert oder nicht erreichbar"
                )
    except Exception as e:
        return TestResult(
            name="Matrix Federation API",
            description="Prueft ob die Federation-API fuer Server-zu-Server Kommunikation bereit ist",
            expected="HTTP 200 mit Server-Info",
            actual=f"Nicht verfuegbar",
            status="skipped",
            duration_ms=(time.time() - start) * 1000,
            error_message=f"Federation-Endpoint nicht erreichbar: {str(e)}"
        )


async def test_matrix_media_api() -> TestResult:
    """Test Matrix Media Repository"""
    start = time.time()
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.get(f"{MATRIX_HOMESERVER}/_matrix/media/v3/config")
            duration = (time.time() - start) * 1000

            if response.status_code == 200:
                data = response.json()
                max_size = data.get("m.upload.size", 0)
                max_mb = max_size / (1024 * 1024) if max_size else "unbegrenzt"
                return TestResult(
                    name="Matrix Media Repository",
                    description="Prueft ob Datei-Uploads (Bilder, Dokumente) moeglich sind",
                    expected="Media-Konfiguration verfuegbar",
                    actual=f"Max Upload: {max_mb} MB",
                    status="passed",
                    duration_ms=duration
                )
            else:
                return TestResult(
                    name="Matrix Media Repository",
                    description="Prueft ob Datei-Uploads (Bilder, Dokumente) moeglich sind",
                    expected="Media-Konfiguration verfuegbar",
                    actual=f"HTTP {response.status_code}",
                    status="failed",
                    duration_ms=duration,
                    error_message="Media API nicht verfuegbar"
                )
    except Exception as e:
        return TestResult(
            name="Matrix Media Repository",
            description="Prueft ob Datei-Uploads (Bilder, Dokumente) moeglich sind",
            expected="Media-Konfiguration verfuegbar",
            actual=f"Fehler: {str(e)}",
            status="failed",
            duration_ms=(time.time() - start) * 1000,
            error_message=str(e)
        )


async def test_jitsi_health() -> TestResult:
    """Test Jitsi Meet Server Health"""
    start = time.time()
    try:
        async with httpx.AsyncClient(timeout=10.0, verify=False) as client:
            # Jitsi health check endpoint
            response = await client.get(f"{JITSI_URL}/http-bind")
            duration = (time.time() - start) * 1000

            # Jitsi returns various status codes, 200 or 404 means server is up
            if response.status_code in [200, 404, 400]:
                return TestResult(
                    name="Jitsi Meet Server",
                    description="Prueft ob der Jitsi Video-Konferenz Server erreichbar ist",
                    expected="Jitsi Server antwortet",
                    actual=f"Server erreichbar (HTTP {response.status_code})",
                    status="passed",
                    duration_ms=duration
                )
            else:
                return TestResult(
                    name="Jitsi Meet Server",
                    description="Prueft ob der Jitsi Video-Konferenz Server erreichbar ist",
                    expected="Jitsi Server antwortet",
                    actual=f"HTTP {response.status_code}",
                    status="failed",
                    duration_ms=duration,
                    error_message=f"Unerwartete Antwort: {response.status_code}"
                )
    except Exception as e:
        return TestResult(
            name="Jitsi Meet Server",
            description="Prueft ob der Jitsi Video-Konferenz Server erreichbar ist",
            expected="Jitsi Server antwortet",
            actual=f"Nicht erreichbar",
            status="failed",
            duration_ms=(time.time() - start) * 1000,
            error_message=str(e)
        )


async def test_jitsi_xmpp() -> TestResult:
    """Test Jitsi XMPP/Prosody"""
    start = time.time()
    try:
        async with httpx.AsyncClient(timeout=10.0, verify=False) as client:
            # Check if Prosody/XMPP is responding
            response = await client.get(f"{JITSI_URL}/xmpp-websocket",
                                        headers={"Upgrade": "websocket"})
            duration = (time.time() - start) * 1000

            # WebSocket upgrade will typically return 400 without proper handshake
            # but that means the endpoint exists
            if response.status_code in [101, 400, 426]:
                return TestResult(
                    name="Jitsi XMPP/Prosody",
                    description="Prueft ob der XMPP Messaging Server fuer Echtzeit-Kommunikation bereit ist",
                    expected="WebSocket-Endpoint verfuegbar",
                    actual=f"XMPP-WebSocket aktiv",
                    status="passed",
                    duration_ms=duration
                )
            else:
                return TestResult(
                    name="Jitsi XMPP/Prosody",
                    description="Prueft ob der XMPP Messaging Server fuer Echtzeit-Kommunikation bereit ist",
                    expected="WebSocket-Endpoint verfuegbar",
                    actual=f"HTTP {response.status_code}",
                    status="skipped",
                    duration_ms=duration,
                    error_message="XMPP-WebSocket nicht konfiguriert"
                )
    except Exception as e:
        return TestResult(
            name="Jitsi XMPP/Prosody",
            description="Prueft ob der XMPP Messaging Server fuer Echtzeit-Kommunikation bereit ist",
            expected="WebSocket-Endpoint verfuegbar",
            actual=f"Nicht verfuegbar",
            status="skipped",
            duration_ms=(time.time() - start) * 1000,
            error_message=str(e)
        )


async def test_communication_api_health() -> TestResult:
    """Test Communication API im Consent Service"""
    start = time.time()
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.get(f"{CONSENT_SERVICE_URL}/api/v1/communication/admin/stats")
            duration = (time.time() - start) * 1000

            if response.status_code == 200:
                data = response.json()
                return TestResult(
                    name="Communication Admin API",
                    description="Prueft ob die Communication-Verwaltungs-API im Backend verfuegbar ist",
                    expected="HTTP 200 mit Statistiken",
                    actual=f"API verfuegbar, Stats geladen",
                    status="passed",
                    duration_ms=duration
                )
            else:
                return TestResult(
                    name="Communication Admin API",
                    description="Prueft ob die Communication-Verwaltungs-API im Backend verfuegbar ist",
                    expected="HTTP 200 mit Statistiken",
                    actual=f"HTTP {response.status_code}",
                    status="failed",
                    duration_ms=duration,
                    error_message=f"API-Fehler: {response.status_code}"
                )
    except Exception as e:
        return TestResult(
            name="Communication Admin API",
            description="Prueft ob die Communication-Verwaltungs-API im Backend verfuegbar ist",
            expected="HTTP 200 mit Statistiken",
            actual=f"Fehler: {str(e)}",
            status="failed",
            duration_ms=(time.time() - start) * 1000,
            error_message=str(e)
        )


async def test_matrix_rooms_db() -> TestResult:
    """Test Matrix Rooms Datenbank-Tabelle"""
    start = time.time()
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            # Check if consent service can access matrix_rooms table
            response = await client.get(f"{CONSENT_SERVICE_URL}/health")
            duration = (time.time() - start) * 1000

            if response.status_code == 200:
                return TestResult(
                    name="Matrix Rooms Datenbank",
                    description="Prueft ob die matrix_rooms Tabelle fuer Raum-Verwaltung existiert",
                    expected="Datenbank-Tabelle verfuegbar",
                    actual="Consent Service gesund, DB-Zugriff OK",
                    status="passed",
                    duration_ms=duration
                )
            else:
                return TestResult(
                    name="Matrix Rooms Datenbank",
                    description="Prueft ob die matrix_rooms Tabelle fuer Raum-Verwaltung existiert",
                    expected="Datenbank-Tabelle verfuegbar",
                    actual=f"Service-Fehler: HTTP {response.status_code}",
                    status="failed",
                    duration_ms=duration,
                    error_message="Consent Service nicht gesund"
                )
    except Exception as e:
        return TestResult(
            name="Matrix Rooms Datenbank",
            description="Prueft ob die matrix_rooms Tabelle fuer Raum-Verwaltung existiert",
            expected="Datenbank-Tabelle verfuegbar",
            actual=f"Fehler: {str(e)}",
            status="failed",
            duration_ms=(time.time() - start) * 1000,
            error_message=str(e)
        )


# ==============================================
# Category Runners
# ==============================================

async def run_matrix_tests() -> TestCategoryResult:
    """Run all Matrix-related tests"""
    tests = await asyncio.gather(
        test_matrix_health(),
        test_matrix_federation(),
        test_matrix_media_api(),
        test_matrix_rooms_db(),
    )

    passed = sum(1 for t in tests if t.status == "passed")
    failed = sum(1 for t in tests if t.status == "failed")

    return TestCategoryResult(
        category="matrix",
        display_name="Matrix Messenger",
        description="Tests fuer den dezentralen Matrix Synapse Server",
        tests=list(tests),
        passed=passed,
        failed=failed,
        total=len(tests)
    )


async def run_jitsi_tests() -> TestCategoryResult:
    """Run all Jitsi-related tests"""
    tests = await asyncio.gather(
        test_jitsi_health(),
        test_jitsi_xmpp(),
    )

    passed = sum(1 for t in tests if t.status == "passed")
    failed = sum(1 for t in tests if t.status == "failed")

    return TestCategoryResult(
        category="jitsi",
        display_name="Jitsi Video",
        description="Tests fuer den Jitsi Meet Video-Konferenz Server",
        tests=list(tests),
        passed=passed,
        failed=failed,
        total=len(tests)
    )


async def run_api_tests() -> TestCategoryResult:
    """Run Communication API tests"""
    tests = await asyncio.gather(
        test_communication_api_health(),
    )

    passed = sum(1 for t in tests if t.status == "passed")
    failed = sum(1 for t in tests if t.status == "failed")

    return TestCategoryResult(
        category="api-health",
        display_name="Communication API",
        description="Tests fuer die Communication-Verwaltungs-Endpunkte",
        tests=list(tests),
        passed=passed,
        failed=failed,
        total=len(tests)
    )


# ==============================================
# API Endpoints
# ==============================================

@router.post("/{category}", response_model=TestCategoryResult)
async def run_category_tests(category: str):
    """Run tests for a specific category"""
    runners = {
        "api-health": run_api_tests,
        "matrix": run_matrix_tests,
        "jitsi": run_jitsi_tests,
    }

    if category not in runners:
        return TestCategoryResult(
            category=category,
            display_name=f"Unbekannt: {category}",
            description="Kategorie nicht gefunden",
            tests=[],
            passed=0,
            failed=0,
            total=0
        )

    return await runners[category]()


@router.post("/run-all", response_model=FullTestResults)
async def run_all_tests():
    """Run all communication tests"""
    start = time.time()

    categories = await asyncio.gather(
        run_api_tests(),
        run_matrix_tests(),
        run_jitsi_tests(),
    )

    total_passed = sum(c.passed for c in categories)
    total_failed = sum(c.failed for c in categories)
    total_tests = sum(c.total for c in categories)

    return FullTestResults(
        categories=list(categories),
        total_passed=total_passed,
        total_failed=total_failed,
        total_tests=total_tests,
        duration_ms=(time.time() - start) * 1000
    )


@router.get("/categories")
async def get_categories():
    """Get available test categories"""
    return {
        "categories": [
            {"id": "api-health", "name": "Communication API", "description": "Backend API Tests"},
            {"id": "matrix", "name": "Matrix Messenger", "description": "Synapse Server Tests"},
            {"id": "jitsi", "name": "Jitsi Video", "description": "Video-Konferenz Tests"},
        ]
    }
