"""Covers the self-contained JWT auth path (app/core/auth.py,
app/core/security.py, app/routers/auth.py) — the actual vulnerability fixed
in Pillar 1 was that ANY caller could impersonate any tenant/user by simply
sending X-Tenant-Id/X-User-Id headers with no verification. These tests
pin down that a token is now required, must be validly signed, and must not
be expired."""

import uuid

import jose.jwt

from app.core.config import settings


async def test_login_success(client, test_tenant):
    resp = await client.post(
        "/auth/login",
        json={
            "tenant_id": str(test_tenant["tenant_id"]),
            "email": test_tenant["admin_email"],
            "password": test_tenant["admin_password"],
        },
    )
    assert resp.status_code == 200
    body = resp.json()
    assert body["access_token"]
    assert body["user"]["email"] == test_tenant["admin_email"]
    assert body["tenant"]["id"] == str(test_tenant["tenant_id"])


async def test_login_wrong_password(client, test_tenant):
    resp = await client.post(
        "/auth/login",
        json={"tenant_id": str(test_tenant["tenant_id"]), "email": test_tenant["admin_email"], "password": "not-the-password"},
    )
    assert resp.status_code == 401


async def test_login_unknown_email(client, test_tenant):
    resp = await client.post(
        "/auth/login",
        json={"tenant_id": str(test_tenant["tenant_id"]), "email": "nobody@example.com", "password": "whatever"},
    )
    assert resp.status_code == 401


async def test_login_unknown_tenant(client):
    resp = await client.post(
        "/auth/login",
        json={"tenant_id": str(uuid.uuid4()), "email": "nobody@example.com", "password": "whatever"},
    )
    assert resp.status_code == 401


async def test_protected_route_without_token_is_rejected(client):
    resp = await client.get("/masters")
    assert resp.status_code == 401


async def test_protected_route_with_malformed_header_is_rejected(client):
    resp = await client.get("/masters", headers={"Authorization": "NotBearer sometoken"})
    assert resp.status_code == 401


async def test_old_header_trust_scheme_no_longer_works(client, test_tenant):
    """The pre-Pillar-1 vulnerability: X-Tenant-Id/X-User-Id headers alone
    used to be trusted with zero verification. Confirms that path is gone."""
    resp = await client.get(
        "/masters",
        headers={
            "X-Tenant-Id": str(test_tenant["tenant_id"]),
            "X-User-Id": str(test_tenant["admin_id"]),
        },
    )
    assert resp.status_code == 401


async def test_protected_route_with_tampered_token_is_rejected(client, admin_token):
    tampered = admin_token[:-4] + ("aaaa" if not admin_token.endswith("aaaa") else "bbbb")
    resp = await client.get("/masters", headers={"Authorization": f"Bearer {tampered}"})
    assert resp.status_code == 401


async def test_protected_route_with_expired_token_is_rejected(client, test_tenant):
    from datetime import datetime, timedelta, timezone

    expired_claims = {
        "tenant_id": str(test_tenant["tenant_id"]),
        "user_id": str(test_tenant["admin_id"]),
        "vendor_id": None,
        "exp": datetime.now(timezone.utc) - timedelta(minutes=1),
    }
    expired_token = jose.jwt.encode(expired_claims, settings.jwt_secret, algorithm=settings.jwt_algorithm)
    resp = await client.get("/masters", headers={"Authorization": f"Bearer {expired_token}"})
    assert resp.status_code == 401


async def test_protected_route_with_valid_token_succeeds(client, auth_headers):
    resp = await client.get("/masters", headers=auth_headers)
    assert resp.status_code == 200


async def test_token_signed_with_wrong_secret_is_rejected(client, test_tenant):
    bogus_token = jose.jwt.encode(
        {"tenant_id": str(test_tenant["tenant_id"]), "user_id": str(test_tenant["admin_id"]), "vendor_id": None},
        "a-completely-different-secret",
        algorithm=settings.jwt_algorithm,
    )
    resp = await client.get("/masters", headers={"Authorization": f"Bearer {bogus_token}"})
    assert resp.status_code == 401


# ---- reCAPTCHA (app/core/recaptcha.py) ----------------------------------------------------

def _enable_recaptcha(monkeypatch) -> None:
    monkeypatch.setattr(settings, "recaptcha_site_key", "test-site-key")
    monkeypatch.setattr(settings, "recaptcha_secret_key", "test-secret-key")


async def test_recaptcha_config_disabled_by_default(client):
    resp = await client.get("/auth/recaptcha-config")
    assert resp.status_code == 200
    assert resp.json() == {"enabled": False, "site_key": ""}


async def test_recaptcha_config_reports_the_public_site_key_once_configured(client, monkeypatch):
    _enable_recaptcha(monkeypatch)
    resp = await client.get("/auth/recaptcha-config")
    assert resp.status_code == 200
    assert resp.json() == {"enabled": True, "site_key": "test-site-key"}
    # never the secret key
    assert "test-secret-key" not in resp.text


async def test_login_still_works_unchanged_when_recaptcha_is_not_configured(client, test_tenant):
    # No recaptcha_token in the payload at all — the field is optional.
    resp = await client.post(
        "/auth/login",
        json={"tenant_id": str(test_tenant["tenant_id"]), "email": test_tenant["admin_email"], "password": test_tenant["admin_password"]},
    )
    assert resp.status_code == 200


async def test_login_requires_a_recaptcha_token_once_configured(client, test_tenant, monkeypatch):
    _enable_recaptcha(monkeypatch)
    resp = await client.post(
        "/auth/login",
        json={"tenant_id": str(test_tenant["tenant_id"]), "email": test_tenant["admin_email"], "password": test_tenant["admin_password"]},
    )
    assert resp.status_code == 400
    assert "reCAPTCHA" in resp.json()["detail"]


async def test_login_rejects_a_token_google_says_is_invalid(client, test_tenant, monkeypatch):
    _enable_recaptcha(monkeypatch)
    monkeypatch.setattr("app.core.recaptcha._sync_verify", lambda token, remote_ip: {"success": False, "error-codes": ["invalid-input-response"]})
    resp = await client.post(
        "/auth/login",
        json={
            "tenant_id": str(test_tenant["tenant_id"]),
            "email": test_tenant["admin_email"],
            "password": test_tenant["admin_password"],
            "recaptcha_token": "bad-token",
        },
    )
    assert resp.status_code == 400
    assert "verification failed" in resp.json()["detail"]


async def test_login_succeeds_once_recaptcha_and_credentials_both_pass(client, test_tenant, monkeypatch):
    _enable_recaptcha(monkeypatch)
    calls = []

    def fake_verify(token, remote_ip):
        calls.append((token, remote_ip))
        return {"success": True}

    monkeypatch.setattr("app.core.recaptcha._sync_verify", fake_verify)
    resp = await client.post(
        "/auth/login",
        json={
            "tenant_id": str(test_tenant["tenant_id"]),
            "email": test_tenant["admin_email"],
            "password": test_tenant["admin_password"],
            "recaptcha_token": "good-token",
        },
    )
    assert resp.status_code == 200, resp.text
    assert resp.json()["access_token"]
    assert len(calls) == 1 and calls[0][0] == "good-token"  # remote_ip is whatever the test transport reports


async def test_login_reports_503_when_google_cant_be_reached(client, test_tenant, monkeypatch):
    _enable_recaptcha(monkeypatch)

    def fake_verify(token, remote_ip):
        import httpx

        raise httpx.ConnectError("connection refused")

    monkeypatch.setattr("app.core.recaptcha._sync_verify", fake_verify)
    resp = await client.post(
        "/auth/login",
        json={
            "tenant_id": str(test_tenant["tenant_id"]),
            "email": test_tenant["admin_email"],
            "password": test_tenant["admin_password"],
            "recaptcha_token": "some-token",
        },
    )
    assert resp.status_code == 503


async def test_wrong_password_still_wins_over_recaptcha_success(client, test_tenant, monkeypatch):
    """reCAPTCHA passing doesn't bypass the actual credential check."""
    _enable_recaptcha(monkeypatch)
    monkeypatch.setattr("app.core.recaptcha._sync_verify", lambda token, remote_ip: {"success": True})
    resp = await client.post(
        "/auth/login",
        json={
            "tenant_id": str(test_tenant["tenant_id"]),
            "email": test_tenant["admin_email"],
            "password": "definitely-wrong",
            "recaptcha_token": "good-token",
        },
    )
    assert resp.status_code == 401
