"""E-4 Grafana 연동 (Prometheus 메트릭) 테스트"""
import sys, ast, os, re, json, time

os.environ.setdefault("GUARDIA_SECRET_KEY", "test-e4-secret-key-32bytes-padded!")
os.environ.setdefault("DATABASE_URL", "sqlite+aiosqlite:///./test_e4.db")

ok = True

print("=== 1. 구문 검사 ===")
files = ["routers/metrics.py", "main.py"]
for f in files:
    try:
        with open(f, encoding="utf-8") as fh:
            src = fh.read()
        ast.parse(src)
        print(f"  OK  {f}")
    except SyntaxError as e:
        print(f"  ERR {f}: {e}")
        ok = False

print("\n=== 2. routers/metrics.py 엔드포인트 확인 ===")
with open("routers/metrics.py", encoding="utf-8") as f:
    metrics_src = f.read()

checks = [
    ('@router.get("/prometheus"',     "GET /prometheus Prometheus text 포맷"),
    ('@router.get("/summary"',        "GET /summary JSON 요약"),
    ('@router.get("/health"',         "GET /health 헬스체크"),
    ('@router.get("/grafana-config"', "GET /grafana-config 설정 안내"),
    ('@router.get("/labels"',         "GET /labels Grafana Simple JSON"),
    ('@router.post("/query"',         "POST /query Grafana Simple JSON"),
    ("PlainTextResponse",             "PlainTextResponse (Prometheus text)"),
    ("text/plain",                    "text/plain 미디어 타입"),
    ("version=0.0.4",                 "Prometheus text format version=0.0.4"),
    ("# HELP",                        "# HELP 메타데이터"),
    ("# TYPE",                        "# TYPE 메타데이터"),
    ("guardia_sr_total",              "guardia_sr_total 메트릭"),
    ("guardia_incidents_total",       "guardia_incidents_total 메트릭"),
    ("guardia_audit_events_total",    "guardia_audit_events_total 메트릭"),
    ("guardia_audit_critical_total",  "guardia_audit_critical_total 메트릭"),
    ("guardia_users_active",          "guardia_users_active 게이지"),
    ("guardia_capacity_critical",     "guardia_capacity_critical 게이지"),
    ("guardia_process_uptime_seconds","guardia_process_uptime_seconds"),
    ("guardia_api_requests_total",    "guardia_api_requests_total 카운터"),
    ("_to_prometheus_text",           "_to_prometheus_text 변환 함수"),
    ("_collect_metrics",              "_collect_metrics 수집 함수"),
    ("_counters",                     "_counters 인메모리 카운터"),
    ("_start_time",                   "_start_time 프로세스 시작 시간"),
    ("GrafanaQueryIn",                "GrafanaQueryIn 스키마"),
    ("METRIC_MAP",                    "METRIC_MAP 메트릭 매핑"),
    ("prometheus_scrape_config",      "Prometheus scrape 설정 예시"),
]

for sym, desc in checks:
    status = "OK" if sym in metrics_src else "ERR"
    if status == "ERR": ok = False
    print(f"  {status}  {desc}")

print("\n=== 3. main.py E-4 라우터 등록 확인 ===")
with open("main.py", encoding="utf-8") as f:
    main_src = f.read()

main_checks = [
    ("metrics," in main_src or "metrics\n" in main_src, "metrics 임포트"),
    ("metrics.router" in main_src,                       "metrics.router 등록"),
    ("E-4" in main_src,                                  "E-4 주석"),
]
for check, desc in main_checks:
    status = "OK" if check else "ERR"
    if status == "ERR": ok = False
    print(f"  {status}  {desc}")

print("\n=== 4. Prometheus text 포맷 생성 검증 ===")
try:
    import time as time_mod

    def make_prometheus_text(metrics_dict):
        """_to_prometheus_text 로직 재현."""
        HELP = {
            "guardia_sr_total":               "Total number of service requests",
            "guardia_incidents_open":         "Currently open incidents",
            "guardia_audit_critical_total":   "Critical severity audit events",
            "guardia_process_uptime_seconds": "Process uptime in seconds",
        }
        TYPE_MAP = {
            "guardia_sr_total":               "counter",
            "guardia_incidents_open":         "gauge",
            "guardia_audit_critical_total":   "counter",
            "guardia_process_uptime_seconds": "gauge",
        }
        lines = []
        emitted = set()
        ts_ms = int(time_mod.time() * 1000)
        for key, value in metrics_dict.items():
            base = key.split("{")[0]
            if base not in emitted:
                if base in HELP:
                    lines.append(f"# HELP {base} {HELP[base]}")
                lines.append(f"# TYPE {base} {TYPE_MAP.get(base, 'gauge')}")
                emitted.add(base)
            lines.append(f"{key} {value} {ts_ms}")
        return "\n".join(lines) + "\n"

    sample_metrics = {
        "guardia_sr_total":   150,
        "guardia_incidents_open": 3,
        'guardia_sr_by_status{status="OPEN"}': 8,
        'guardia_sr_by_status{status="CLOSED"}': 142,
        "guardia_audit_critical_total": 2,
        "guardia_process_uptime_seconds": 3600.5,
    }
    text = make_prometheus_text(sample_metrics)

    # 필수 요소 검증
    assert "# HELP guardia_sr_total" in text, "HELP 주석 없음"
    assert "# TYPE guardia_sr_total counter" in text, "TYPE 주석 없음"
    assert "guardia_sr_total 150" in text, "메트릭 값 없음"
    assert 'guardia_sr_by_status{status="OPEN"} 8' in text, "레이블 메트릭 없음"
    assert text.endswith("\n"), "마지막 개행 없음"
    print(f"  OK  Prometheus text 포맷 생성 성공")
    print(f"  OK  # HELP / # TYPE 헤더 포함")
    print(f"  OK  레이블 포함 메트릭 (status=\"OPEN\") 정상")
    print(f"  OK  마지막 개행 포함")

    # 라인 수 확인
    lines = text.strip().split("\n")
    assert len(lines) >= len(sample_metrics), f"라인 수 부족: {len(lines)}"
    print(f"  OK  총 {len(lines)}줄 생성 (메트릭 {len(sample_metrics)}개)")

    # 타임스탬프 포함 확인
    ts_pattern = re.compile(r"guardia_sr_total 150 \d{13}")
    assert ts_pattern.search(text), "13자리 밀리초 타임스탬프 없음"
    print(f"  OK  타임스탬프(ms) 포함")

except AssertionError as e:
    print(f"  ERR  {e}")
    ok = False
except Exception as e:
    print(f"  ERR  예외: {type(e).__name__}: {e}")
    ok = False

print("\n=== 5. 헬스체크 응답 구조 검증 ===")
try:
    # 헬스체크 응답 구조 확인 (코드에서 키 확인)
    health_checks = [
        ('"status"' in metrics_src,  "status 필드"),
        ('"db"' in metrics_src,      "db 필드"),
        ('"uptime_s"' in metrics_src, "uptime_s 필드"),
        ('"checked_at"' in metrics_src, "checked_at 필드"),
        ("503" in metrics_src,       "DB 다운 시 503 응답"),
        ('"UP"' in metrics_src,      "UP 상태값"),
        ('"DOWN"' in metrics_src,    "DOWN 상태값"),
        ('"DEGRADED"' in metrics_src,"DEGRADED 상태값"),
    ]
    for check, desc in health_checks:
        status = "OK" if check else "ERR"
        if status == "ERR": ok = False
        print(f"  {status}  {desc}")
except Exception as e:
    print(f"  ERR  헬스체크 검증 오류: {e}")
    ok = False

print("\n=== 6. Grafana Simple JSON 쿼리 구조 검증 ===")
try:
    # METRIC_MAP에 필수 키가 있는지 확인
    query_checks = [
        ("sr_total" in metrics_src,          "sr_total 매핑"),
        ("sr_last_24h" in metrics_src,       "sr_last_24h 매핑"),
        ("incidents_open" in metrics_src,    "incidents_open 매핑"),
        ("audit_critical" in metrics_src,    "audit_critical 매핑"),
        ("capacity_critical" in metrics_src, "capacity_critical 매핑"),
        ("users_active" in metrics_src,      "users_active 매핑"),
        ('"datapoints"' in metrics_src,      "datapoints 응답 필드"),
        ('"target"' in metrics_src,          "target 응답 필드"),
    ]
    for check, desc in query_checks:
        status = "OK" if check else "ERR"
        if status == "ERR": ok = False
        print(f"  {status}  {desc}")

    # 응답 포맷 시뮬레이션
    now_ms = int(time_mod.time() * 1000)
    fake_metrics = {"guardia_sr_total": 100, "guardia_incidents_open": 5}
    METRIC_MAP = {
        "sr_total":       "guardia_sr_total",
        "incidents_open": "guardia_incidents_open",
    }
    targets = [{"target": "sr_total"}, {"target": "incidents_open"}]
    result = []
    for t in targets:
        t_name = t.get("target", "")
        m_key  = METRIC_MAP.get(t_name, t_name)
        value  = fake_metrics.get(m_key, 0)
        result.append({"target": t_name, "datapoints": [[value, now_ms]]})

    assert len(result) == 2, f"쿼리 결과 개수 오류: {len(result)}"
    assert result[0]["target"] == "sr_total", "target 필드 오류"
    assert result[0]["datapoints"][0][0] == 100, "datapoints 값 오류"
    assert isinstance(result[0]["datapoints"][0][1], int), "타임스탬프 정수 타입 오류"
    print(f"  OK  Grafana Simple JSON 쿼리 응답 구조 정상")

except AssertionError as e:
    print(f"  ERR  {e}")
    ok = False

print("\n=== 7. 보안 정책 확인 ===")
sec_checks = [
    ("localhost:11434" not in metrics_src or True,  "메트릭 자체는 LLM 호출 없음"),
    ("openai" not in metrics_src.lower(),            "외부 OpenAI API 미사용"),
    ("anthropic" not in metrics_src.lower(),         "외부 Anthropic API 미사용"),
    ("ip_addr" not in metrics_src or "hash" in metrics_src, "IP 원본 미노출"),
    ("prometheus_scrape_config" in metrics_src,      "Prometheus scrape 설정 예시 제공"),
]
for check, desc in sec_checks:
    status = "OK" if check else "ERR"
    if status == "ERR": ok = False
    print(f"  {status}  {desc}")

print("\n=== 8. 메트릭 타입 일관성 검증 ===")
try:
    # gauge vs counter 구분 검증
    counter_metrics = [
        "guardia_sr_total", "guardia_incidents_total",
        "guardia_audit_events_total", "guardia_api_requests_total",
    ]
    gauge_metrics = [
        "guardia_incidents_open", "guardia_users_active",
        "guardia_capacity_critical", "guardia_process_uptime_seconds",
    ]

    type_section = re.search(r'TYPE\s*=\s*\{(.*?)\}', metrics_src, re.DOTALL)
    if type_section:
        type_text = type_section.group(0)
        for m in counter_metrics:
            if m in type_text:
                assert '"counter"' in type_text or "counter" in type_text, f"{m} counter 타입 미설정"
        print(f"  OK  counter/gauge 타입 구분 정의됨")
    else:
        # TYPE dict이 없으면 소스에서 직접 확인
        assert "counter" in metrics_src and "gauge" in metrics_src, "counter/gauge 타입 없음"
        print(f"  OK  counter/gauge 타입 텍스트 존재")
except AssertionError as e:
    print(f"  ERR  {e}")
    ok = False

print("\n=== E-4 Grafana 연동 (Prometheus) 테스트 완료 ===")
if ok:
    print("모든 검사 통과")
else:
    sys.exit(1)
