"""
OLLAMA URL endpoint poisoning 防護測試 — Wave8-X2 + ADR-110

vuln #1：OLLAMA_URL / OLLAMA_FALLBACK_URL 缺 IP allowlist 校驗
修法：pydantic field_validator 拒絕非 private/loopback/known-hostname

2026-04-27 Wave8-X2 by Claude — vuln #1 + B14 + alerter memory dedup
2026-05-03 ogt: ADR-110 GCP 三層容災，更新 fixture 預設值為 GCP-A，新增 GCP 白名單測試
"""

from __future__ import annotations

import pytest
from pydantic import ValidationError


# =============================================================================
# Helpers
# =============================================================================


def _make_settings(**kwargs):
    """建立 Settings 實例，只覆蓋指定欄位，其餘用安全預設值。"""
    from src.core.config import Settings

    base = {
        "DATABASE_URL": "postgresql://u:p@localhost:5432/test",
        "OLLAMA_URL": "http://34.143.170.20:11434",   # GCP-A Primary（ADR-110 2026-05-03）
        "OLLAMA_FALLBACK_URL": "",
    }
    base.update(kwargs)
    return Settings(**base)


# =============================================================================
# #1: 公網 IP 應被拒絕
# =============================================================================


def test_public_ip_rejected_in_ollama_url():
    """8.8.8.8 是公網 IP，應被 validator 拒絕（端點中毒攻擊情境）"""
    with pytest.raises(ValidationError, match="公網"):
        _make_settings(OLLAMA_URL="http://8.8.8.8:11434")


def test_public_ip_rejected_in_ollama_fallback_url():
    """FALLBACK_URL 也受同一 validator 保護"""
    with pytest.raises(ValidationError, match="公網"):
        _make_settings(OLLAMA_FALLBACK_URL="http://1.1.1.1:11434")


# =============================================================================
# #2: 外部域名應被拒絕
# =============================================================================


def test_external_domain_rejected():
    """attacker.com 是外部域名（非 IP，非白名單），應被拒絕"""
    with pytest.raises(ValidationError, match="外部域名"):
        _make_settings(OLLAMA_URL="http://attacker.com:11434")


def test_external_domain_fallback_rejected():
    """FALLBACK_URL 外部域名也應被拒絕"""
    with pytest.raises(ValidationError, match="外部域名"):
        _make_settings(OLLAMA_FALLBACK_URL="http://evil.example.com:11434")


# =============================================================================
# #3: GCP 白名單公網 IP 應通過（ADR-110 2026-05-03）
# =============================================================================


def test_gcp_a_public_ip_accepted():
    """34.143.170.20 是 GCP-A 核准公網 IP（ADR-110 白名單），應通過"""
    s = _make_settings(OLLAMA_URL="http://34.143.170.20:11434")
    assert s.OLLAMA_URL == "http://34.143.170.20:11434"


def test_gcp_b_public_ip_accepted():
    """34.21.145.224 是 GCP-B 核准公網 IP（ADR-110 白名單），應通過"""
    s = _make_settings(OLLAMA_URL="http://34.21.145.224:11434")
    assert s.OLLAMA_URL == "http://34.21.145.224:11434"


def test_gcp_b_as_secondary_url_accepted():
    """GCP-B 作為 OLLAMA_SECONDARY_URL 也應通過白名單"""
    s = _make_settings(
        OLLAMA_URL="http://34.143.170.20:11434",
        OLLAMA_SECONDARY_URL="http://34.21.145.224:11434",
    )
    assert s.OLLAMA_SECONDARY_URL == "http://34.21.145.224:11434"


def test_arbitrary_public_ip_rejected():
    """8.8.8.8 非 GCP 白名單公網 IP，仍應被拒絕（端點中毒攻擊防護）"""
    with pytest.raises(ValidationError, match="公網"):
        _make_settings(OLLAMA_URL="http://8.8.8.8:11434")


# =============================================================================
# #4: 私網 IP 應通過
# =============================================================================


def test_private_ip_192_168_accepted():
    """192.168.0.111 是 RFC1918 私網 IP（Local HDD 後備主機），應通過"""
    s = _make_settings(OLLAMA_URL="http://192.168.0.111:11434")
    assert s.OLLAMA_URL == "http://192.168.0.111:11434"


def test_private_ip_10_x_accepted():
    """10.x.x.x 是 RFC1918 私網 IP，應通過"""
    s = _make_settings(OLLAMA_URL="http://10.0.0.5:11434")
    assert s.OLLAMA_URL == "http://10.0.0.5:11434"


def test_private_ip_172_16_accepted():
    """172.16.x.x 是 RFC1918 私網 IP，應通過"""
    s = _make_settings(OLLAMA_FALLBACK_URL="http://172.16.0.10:11434")
    assert s.OLLAMA_FALLBACK_URL == "http://172.16.0.10:11434"


# =============================================================================
# #5: localhost / loopback 應通過
# =============================================================================


def test_localhost_accepted():
    """localhost 在 known hostname 白名單，應通過"""
    s = _make_settings(OLLAMA_URL="http://localhost:11434")
    assert s.OLLAMA_URL == "http://localhost:11434"


def test_loopback_ip_accepted():
    """127.0.0.1 是 loopback IP，應通過"""
    s = _make_settings(OLLAMA_URL="http://127.0.0.1:11434")
    assert s.OLLAMA_URL == "http://127.0.0.1:11434"


# =============================================================================
# #6: 已知 K8s Service hostname 應通過
# =============================================================================


def test_known_k8s_svc_ollama_svc_accepted():
    """ollama-svc 在 K8s Service 白名單，應通過"""
    s = _make_settings(OLLAMA_URL="http://ollama-svc:11434")
    assert s.OLLAMA_URL == "http://ollama-svc:11434"


def test_known_k8s_svc_ollama_fallback_svc_accepted():
    """ollama-fallback-svc 在白名單，應通過"""
    s = _make_settings(OLLAMA_FALLBACK_URL="http://ollama-fallback-svc:11434")
    assert s.OLLAMA_FALLBACK_URL == "http://ollama-fallback-svc:11434"


# =============================================================================
# #7: 空字串應通過（OLLAMA_FALLBACK_URL 預設值）
# =============================================================================


def test_empty_string_fallback_url_accepted():
    """OLLAMA_FALLBACK_URL 預設空字串（未設定），應通過"""
    s = _make_settings(OLLAMA_FALLBACK_URL="")
    assert s.OLLAMA_FALLBACK_URL == ""


def test_database_pool_budget_defaults_to_production_safe_values():
    """Production DB role has a tiny connection limit; defaults must not fan out."""
    s = _make_settings()
    assert s.DATABASE_POOL_SIZE == 1
    assert s.DATABASE_MAX_OVERFLOW == 0
    assert s.DATABASE_POOL_TIMEOUT_SECONDS == 5.0
