from fastapi.testclient import TestClient

import app.dependencies as deps
from app.main import app
from app.config import settings

client = TestClient(app)


def _stub_chat(monkeypatch, answer="ok"):
    class StubLLM:
        async def complete(self, text, history=None, session_id=None, language=None):
            return answer

    class StubTTS:
        async def synthesize(self, text, voice=None, audio_format="pcm", language=None):
            return b"A"

    monkeypatch.setitem(deps.LLM_REGISTRY, "l", lambda s: StubLLM())
    monkeypatch.setitem(deps.TTS_REGISTRY, "t", lambda s: StubTTS())
    return {"llm_provider": "l", "tts_provider": "t"}


# --- Quota -----------------------------------------------------------------

def test_quota_blocks_after_limit(monkeypatch):
    base = _stub_chat(monkeypatch)
    monkeypatch.setattr(settings, "daily_request_limit", 1)
    body = {"text": "Hallo", **base}
    assert client.post("/api/chat?debug=true", json=body).status_code == 200
    assert client.post("/api/chat?debug=true", json=body).status_code == 429


def test_quota_unlimited_by_default(monkeypatch):
    base = _stub_chat(monkeypatch)
    body = {"text": "Hallo", **base}
    for _ in range(3):
        assert client.post("/api/chat?debug=true", json=body).status_code == 200


# --- Manueller Notruf (vom Nutzer ausgelöst) -------------------------------

def test_manual_emergency_logs_and_returns_localized_notice():
    resp = client.post("/api/emergency", json={"language": "de"})
    assert resp.status_code == 200
    data = resp.json()
    assert data["category"] == "manual"
    assert "noch keine Benachrichtigung" in data["notice"]
    # Notruf wurde protokolliert
    events = deps.get_store().list_emergency_events(10)
    assert any(e["category"] == "manual" for e in events)


def test_manual_emergency_notice_language_fallback():
    # Unbekannte Sprache -> deutscher Fallback-Hinweis
    en = client.post("/api/emergency", json={"language": "en"}).json()["notice"]
    assert "ATTENTION" in en
    xx = client.post("/api/emergency", json={"language": "xx"}).json()["notice"]
    assert "ACHTUNG" in xx


def test_manual_emergency_does_not_count_against_quota(monkeypatch):
    # Der Notruf-Endpunkt ist von der Chat-Quota unabhängig.
    monkeypatch.setattr(settings, "daily_request_limit", 1)
    for _ in range(3):
        assert client.post("/api/emergency", json={"language": "de"}).status_code == 200


def test_manual_emergency_sends_email_when_smtp_configured(monkeypatch):
    import app.safety.emergency as em
    captured = {}

    class FakeSMTP:
        def __init__(self, host, port, timeout=10):
            captured["host"] = host
        def __enter__(self):
            return self
        def __exit__(self, *a):
            return False
        def starttls(self):
            captured["tls"] = True
        def login(self, u, p):
            captured["login"] = u
        def send_message(self, msg):
            captured["to"] = msg["To"]
            captured["subject"] = msg["Subject"]

    monkeypatch.setattr(em.smtplib, "SMTP", FakeSMTP)
    monkeypatch.setattr(settings, "smtp_host", "mail.example.com")
    monkeypatch.setattr(settings, "emergency_contact_email", "kontakt@example.com")

    data = client.post("/api/emergency", json={"language": "de"}).json()
    assert data["email_sent"] is True
    assert "benachrichtigt" in data["notice"].lower()
    assert captured["to"] == "kontakt@example.com"
    assert "Notruf" in captured["subject"]


def test_manual_emergency_posts_webhook_when_configured(monkeypatch):
    import app.safety.emergency as em
    captured = {}

    class FakeResp:
        is_success = True
        status_code = 200

    class FakeClient:
        def __init__(self, *a, **k):
            pass

        async def __aenter__(self):
            return self

        async def __aexit__(self, *a):
            return False

        async def post(self, url, json=None, headers=None):
            captured["url"] = url
            captured["json"] = json
            captured["headers"] = headers
            return FakeResp()

    monkeypatch.setattr(em.httpx, "AsyncClient", FakeClient)
    monkeypatch.setattr(settings, "emergency_webhook_url", "https://hook.example/notify")
    monkeypatch.setattr(settings, "emergency_webhook_token", "s3cret")
    monkeypatch.setattr(settings, "emergency_contact_phone", "+4915112345678")

    data = client.post("/api/emergency", json={"language": "de"}).json()
    assert data["webhook_sent"] is True
    assert data["email_sent"] is False  # kein SMTP -> Webhook trägt allein
    assert "benachrichtigt" in data["notice"].lower()  # ein Kanal reicht für "gesendet"-Hinweis

    assert captured["url"] == "https://hook.example/notify"
    assert captured["headers"]["Authorization"] == "Bearer s3cret"
    payload = captured["json"]
    assert payload["category"] == "manual"
    assert payload["phones"] == ["+4915112345678"]
    assert set(payload["channels"]) >= {"sms", "call"}
    assert "sms" in payload["message"] and "call" in payload["message"]


def test_manual_emergency_no_webhook_without_url(monkeypatch):
    import app.safety.emergency as em

    def _boom(*a, **k):  # AsyncClient darf ohne URL gar nicht erst gebaut werden
        raise AssertionError("Webhook ohne URL nicht aufrufen")

    monkeypatch.setattr(em.httpx, "AsyncClient", _boom)
    monkeypatch.setattr(settings, "emergency_webhook_url", "")
    data = client.post("/api/emergency", json={"language": "de"}).json()
    assert data["webhook_sent"] is False
