"""E-1 월별 리포트 자동 생성 테스트"""
import sys, ast, os, json, re, hashlib
from datetime import datetime
from calendar import monthrange

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

ok = True

print("=== 1. 구문 검사 ===")
files = ["routers/report.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/report.py 엔드포인트 확인 ===")
with open("routers/report.py", encoding="utf-8") as f:
    report_src = f.read()

checks = [
    ('@router.get("/generate"',           "GET /generate 리포트 즉시 생성"),
    ('@router.get("/monthly/{year}/{month}"', "GET /monthly/{year}/{month}"),
    ('@router.get("/list"',               "GET /list 목록"),
    ('@router.get("/preview"',            "GET /preview 미리보기"),
    ('@router.get("/{report_id}"',        "GET /{report_id} 상세"),
    ('@router.get("/export/{report_id}"', "GET /export/{report_id} 다운로드"),
    ('@router.post("/schedule"',          "POST /schedule 스케줄 설정"),
    ("generate_monthly_report",           "generate_monthly_report 함수"),
    ("_gather_sr_stats",                  "_gather_sr_stats 통계 함수"),
    ("_gather_audit_stats",               "_gather_audit_stats 감사 함수"),
    ("_gather_capacity_stats",            "_gather_capacity_stats 용량 함수"),
    ("_llm_generate_summary",             "_llm_generate_summary LLM 함수"),
    ("_build_fallback_summary",           "_build_fallback_summary 규칙 요약"),
    ("_build_recommendations",            "_build_recommendations 권고사항"),
    ("localhost:11434",                   "Ollama 내부 LLM (외부 API 금지 준수)"),
    ("StreamingResponse",                 "StreamingResponse (JSON 다운로드)"),
    ("Content-Disposition",               "Content-Disposition 헤더"),
    ("UserRole.ADMIN",                    "ADMIN 권한 제한"),
    ("UserRole.PM",                       "PM 권한 제한"),
    ("ScheduleConfigIn",                  "ScheduleConfigIn 스키마"),
    ("health_score",                      "health_score 헬스 스코어"),
    ("health_grade",                      "health_grade 등급"),
    ("_reports",                          "인메모리 리포트 캐시"),
    ("RPT-",                              "RPT- 리포트 ID 포맷"),
    ("resolution_rate",                   "resolution_rate SR 해결률"),
    ("executive_summary",                 "executive_summary 경영진 요약"),
    ("recommendations",                   "recommendations 권고사항"),
]

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

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

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

print("\n=== 4. 리포트 ID 포맷 검증 ===")
try:
    year, month = 2026, 5
    requester = "admin"
    report_id = f"RPT-{year}{month:02d}-{hashlib.sha256(f'{year}{month}{requester}'.encode()).hexdigest()[:6].upper()}"
    assert report_id.startswith("RPT-"), f"RPT- 접두사 없음: {report_id}"
    assert len(report_id) == 17, f"ID 길이 오류: {len(report_id)} (기대: 17)"
    assert re.match(r"RPT-\d{6}-[A-F0-9]{6}", report_id), f"포맷 불일치: {report_id}"
    print(f"  OK  리포트 ID 생성: {report_id}")
    print(f"  OK  ID 길이: {len(report_id)}자 (기대: 16)")
    print(f"  OK  포맷 정규식 통과: RPT-YYYYMM-XXXXXX")
except AssertionError as e:
    print(f"  ERR  {e}")
    ok = False

print("\n=== 5. 헬스 스코어 알고리즘 검증 ===")
try:
    def calc_health(critical_events, critical_plans, resolution_rate):
        health_deductions = (
            min(30, critical_events * 5) +
            min(20, critical_plans * 10) +
            max(0, 20 - resolution_rate // 5)
        )
        return max(0, 100 - int(health_deductions))

    # 완벽한 운영
    score_perfect = calc_health(0, 0, 100)
    assert score_perfect == 100, f"완벽 점수 오류: {score_perfect}"
    print(f"  OK  완벽 운영: 헬스 스코어 = {score_perfect} (기대: 100)")

    # CRITICAL 이벤트 6건 → -30
    score_critical = calc_health(6, 0, 100)
    assert score_critical == 70, f"CRITICAL 6건 점수 오류: {score_critical}"
    print(f"  OK  CRITICAL 6건: 헬스 스코어 = {score_critical} (기대: 70)")

    # 용량 위험 2개 → -20
    score_capacity = calc_health(0, 2, 100)
    assert score_capacity == 80, f"용량 위험 2개 점수 오류: {score_capacity}"
    print(f"  OK  용량 위험 2개: 헬스 스코어 = {score_capacity} (기대: 80)")

    # SR 해결률 0% → -20
    score_sr = calc_health(0, 0, 0)
    assert score_sr == 80, f"SR 해결률 0% 점수 오류: {score_sr}"
    print(f"  OK  SR 해결률 0%: 헬스 스코어 = {score_sr} (기대: 80)")

    # 최악 시나리오 (최대 감점 30+20+20=70 → 최소 30)
    score_worst = calc_health(100, 100, 0)
    assert score_worst == 30, f"최악 시나리오 점수 오류: {score_worst} (기대: 30)"
    print(f"  OK  최악 시나리오: 헬스 스코어 = {score_worst} (기대: 30, 최대 감점=70)")

    # 등급 구분
    grade_tests = [
        (95, "A"), (90, "A"), (80, "B"), (75, "B"), (65, "C"), (60, "C"), (50, "D"),
    ]
    for score, expected in grade_tests:
        grade = "A" if score >= 90 else "B" if score >= 75 else "C" if score >= 60 else "D"
        assert grade == expected, f"등급 오류: score={score} grade={grade} (기대: {expected})"
    print(f"  OK  등급 구분 (A/B/C/D) 모두 정확")

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

print("\n=== 6. 권고사항 생성 로직 검증 ===")
try:
    def build_recommendations(stats):
        recs = []
        sr = stats.get("sr", {})
        sec = stats.get("security", {})
        cap = stats.get("capacity", {})
        if sr.get("resolution_rate", 100) < 80:
            recs.append(f"SR 해결률 미달")
        if sr.get("open", 0) > 10:
            recs.append(f"미처리 SR 다수")
        if sec.get("critical_events", 0) > 0:
            recs.append(f"CRITICAL 보안 이벤트 조사 필요")
        if cap.get("critical_plans", 0) > 0:
            recs.append(f"용량 위험 시스템 증설 권고")
        if not recs:
            recs.append("운영 지표 정상")
        return recs

    # 정상 운영 → 정상 메시지
    recs_ok = build_recommendations({"sr": {"resolution_rate": 95, "open": 3},
                                      "security": {"critical_events": 0},
                                      "capacity": {"critical_plans": 0}})
    assert len(recs_ok) == 1 and "정상" in recs_ok[0], f"정상 권고 오류: {recs_ok}"
    print(f"  OK  정상 운영: '{recs_ok[0]}'")

    # 문제 다수 → 다건 권고
    recs_multi = build_recommendations({"sr": {"resolution_rate": 60, "open": 15},
                                         "security": {"critical_events": 2},
                                         "capacity": {"critical_plans": 1}})
    assert len(recs_multi) == 4, f"다건 권고 개수 오류: {len(recs_multi)} (기대: 4)"
    print(f"  OK  문제 다수: 권고 {len(recs_multi)}건 생성")

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

print("\n=== 7. Fallback 요약 생성 검증 ===")
try:
    def build_fallback(stats, year, month):
        sr = stats.get("sr", {})
        sec = stats.get("security", {})
        cap = stats.get("capacity", {})
        lines = [
            f"{year}년 {month}월 GUARDiA ITSM 운영 월간 보고서입니다.",
            f"이번 달 총 SR {sr.get('total', 0)}건이 접수되어 해결률 {sr.get('resolution_rate', 0)}%를 달성했습니다.",
            f"보안 감사 이벤트는 총 {sec.get('total_events', 0)}건이며, "
            f"중요(CRITICAL) 이벤트는 {sec.get('critical_events', 0)}건입니다.",
        ]
        if cap.get("critical_plans", 0) > 0:
            lines.append(f"용량 위험 시스템이 {cap['critical_plans']}개 감지되어 즉각적인 조치가 필요합니다.")
        return " ".join(lines)

    summary = build_fallback({
        "sr": {"total": 120, "resolution_rate": 92.5},
        "security": {"total_events": 450, "critical_events": 0},
        "capacity": {"critical_plans": 0},
    }, 2026, 5)
    assert "2026년 5월" in summary, "연월 포함 오류"
    assert "120건" in summary, "SR 건수 포함 오류"
    assert "92.5%" in summary, "해결률 포함 오류"
    print(f"  OK  Fallback 요약 생성 성공")
    print(f"  OK  내용: {summary[:80]}...")

    # 용량 위험 포함
    summary2 = build_fallback({
        "sr": {"total": 50, "resolution_rate": 70.0},
        "security": {"total_events": 100, "critical_events": 3},
        "capacity": {"critical_plans": 2},
    }, 2026, 5)
    assert "용량 위험" in summary2, "용량 위험 문구 포함 오류"
    print(f"  OK  용량 위험 시스템 언급 확인")

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

print("\n=== 8. ScheduleConfigIn 스키마 확인 ===")
schedule_checks = [
    ("send_day",    "send_day 필드"),
    ("recipients",  "recipients 필드"),
    ("include_llm", "include_llm 필드"),
    ("enabled",     "enabled 필드"),
    ("1 <= body.send_day <= 28", "send_day 범위 검증 (1~28)"),
]
for sym, desc in schedule_checks:
    status = "OK" if sym in report_src else "ERR"
    if status == "ERR": ok = False
    print(f"  {status}  {desc}")

print("\n=== 9. 보안 정책 확인 ===")
security_checks = [
    ("localhost:11434" in report_src and "openai" not in report_src.lower()
     and "anthropic" not in report_src.lower(),
     "외부 AI API 미사용 (Ollama only)"),
    ("UserRole.ADMIN" in report_src, "ADMIN 전용 기능 존재"),
    ("attachment; filename=" in report_src, "다운로드 파일명 설정"),
    ("ensure_ascii=False" in report_src, "한글 JSON 인코딩"),
]
for check, desc in security_checks:
    status = "OK" if check else "ERR"
    if status == "ERR": ok = False
    print(f"  {status}  {desc}")

print("\n=== 10. 캘린더 유틸리티 검증 ===")
try:
    # monthrange 정확도
    for m, expected_last in [(1, 31), (2, 28), (4, 30), (12, 31)]:
        _, last = monthrange(2026, m)
        assert last == expected_last, f"{m}월 마지막 날 오류: {last} (기대: {expected_last})"
    # 2024년 2월은 윤년
    _, last_feb_2024 = monthrange(2024, 2)
    assert last_feb_2024 == 29, f"2024년 2월 오류: {last_feb_2024}"
    print(f"  OK  monthrange 정확도 검증 (윤년 포함)")

    # 기간 범위 생성 검증
    year, month = 2026, 5
    _, last_day = monthrange(year, month)
    start_dt = datetime(year, month, 1, 0, 0, 0)
    end_dt   = datetime(year, month, last_day, 23, 59, 59)
    assert start_dt.day == 1, "시작일 오류"
    assert end_dt.day == 31, "종료일 오류 (5월)"
    assert (end_dt - start_dt).days == 30, "기간 오류"
    print(f"  OK  기간 범위: {start_dt.date()} ~ {end_dt.date()}")

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

print("\n=== E-1 월별 리포트 자동 생성 테스트 완료 ===")
if ok:
    print("모든 검사 통과")
else:
    sys.exit(1)
