"""E-5 FinOps 비용 분석 테스트"""
import sys, ast, os, re, json

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

ok = True

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

checks = [
    ('@router.post("/costs"',           "POST /costs 비용 등록"),
    ('@router.get("/costs"',            "GET /costs 비용 목록"),
    ('@router.get("/summary"',          "GET /summary 월별 요약"),
    ('@router.get("/trend"',            "GET /trend 트렌드"),
    ('@router.get("/allocation"',       "GET /allocation 배분"),
    ('@router.get("/anomalies"',        "GET /anomalies 이상 탐지"),
    ('@router.get("/recommendations"',  "GET /recommendations 권고"),
    ('@router.post("/budget"',          "POST /budget 예산 등록"),
    ('@router.get("/budget"',           "GET /budget 예산 대비"),
    ('@router.get("/optimize"',         "GET /optimize AI 최적화"),
    ("COST_CATEGORIES",                 "COST_CATEGORIES 카테고리 정의"),
    ("SERVICES",                        "SERVICES 서비스 목록"),
    ("_costs",                          "_costs 인메모리 저장소"),
    ("_budgets",                        "_budgets 예산 저장소"),
    ("_gen_cost_id",                    "_gen_cost_id ID 생성"),
    ("_filter_costs",                   "_filter_costs 필터 함수"),
    ("_sum_costs",                      "_sum_costs 합계 함수"),
    ("mom_change_pct",                  "mom_change_pct 전월 비교"),
    ("usage_pct",                       "usage_pct 예산 사용률"),
    ("potential_saving_pct",            "potential_saving_pct 절감 가능률"),
    ("localhost:11434",                 "Ollama 내부 LLM (외부 API 금지)"),
    ("UserRole.ADMIN",                  "ADMIN 권한 제한"),
    ("UserRole.PM",                     "PM 권한 제한"),
    ("trend_dir",                       "trend_dir UP/DOWN/STABLE"),
    ("COST-",                           "COST- ID 접두사"),
    ("variance",                        "variance 예산 편차"),
]

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

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

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

print("\n=== 4. 비용 카테고리 및 서비스 구성 검증 ===")
try:
    # 로컬 정의
    COST_CATEGORIES = {
        "SERVER": "서버 운영비", "NETWORK": "네트워크/통신비",
        "STORAGE": "스토리지 비용", "LICENSE": "소프트웨어 라이선스",
        "MAINTENANCE": "유지보수비", "PERSONNEL": "인건비", "OTHER": "기타",
    }
    SERVICES = ["MES", "ERP", "GROUPWARE", "SECURITY", "INFRA", "BACKUP", "OTHER"]

    assert len(COST_CATEGORIES) >= 6, f"카테고리 6개 이상 필요: {len(COST_CATEGORIES)}"
    assert len(SERVICES) >= 5, f"서비스 5개 이상 필요: {len(SERVICES)}"
    assert "SERVER" in COST_CATEGORIES, "SERVER 카테고리 없음"
    assert "LICENSE" in COST_CATEGORIES, "LICENSE 카테고리 없음"
    assert "INFRA" in SERVICES, "INFRA 서비스 없음"
    print(f"  OK  카테고리 {len(COST_CATEGORIES)}개: {list(COST_CATEGORIES)}")
    print(f"  OK  서비스 {len(SERVICES)}개: {SERVICES}")
except AssertionError as e:
    print(f"  ERR  {e}")
    ok = False

print("\n=== 5. 비용 집계 로직 검증 ===")
try:
    # _sum_costs 로직 재현
    def sum_costs(items):
        return round(sum(c["amount"] for c in items), 2)

    def filter_costs(costs, year, month=None, service=None):
        result = []
        for c in costs.values():
            if c["year"] != year: continue
            if month is not None and c["month"] != month: continue
            if service is not None and c["service"] != service: continue
            result.append(c)
        return result

    costs_db = {
        "C1": {"year": 2026, "month": 5, "category": "SERVER", "service": "INFRA", "amount": 1500000},
        "C2": {"year": 2026, "month": 5, "category": "LICENSE", "service": "ERP", "amount": 800000},
        "C3": {"year": 2026, "month": 5, "category": "NETWORK", "service": "INFRA", "amount": 300000},
        "C4": {"year": 2026, "month": 4, "category": "SERVER", "service": "INFRA", "amount": 1400000},
    }

    may_items = filter_costs(costs_db, 2026, 5)
    assert len(may_items) == 3, f"5월 항목 3개 기대: {len(may_items)}"
    assert sum_costs(may_items) == 2600000, f"5월 합계 오류: {sum_costs(may_items)}"
    print(f"  OK  5월 필터: {len(may_items)}건, 합계: {sum_costs(may_items):,}원")

    infra_items = filter_costs(costs_db, 2026, 5, "INFRA")
    assert len(infra_items) == 2, f"INFRA 5월 2건 기대: {len(infra_items)}"
    assert sum_costs(infra_items) == 1800000, f"INFRA 합계 오류"
    print(f"  OK  INFRA 5월 필터: {len(infra_items)}건, {sum_costs(infra_items):,}원")

    # 전월 비교
    apr_total = sum_costs(filter_costs(costs_db, 2026, 4))
    may_total = sum_costs(filter_costs(costs_db, 2026, 5))
    mom_change = round((may_total - apr_total) / apr_total * 100, 1)
    assert mom_change > 0, f"전월 대비 증가 기대: {mom_change}%"
    print(f"  OK  전월 비교: {apr_total:,}원 -> {may_total:,}원 ({mom_change:+.1f}%)")

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

print("\n=== 6. 예산 대비 실적 검증 ===")
try:
    def calc_budget_status(actual, budget):
        if budget <= 0:
            return "NO_BUDGET", 0.0
        usage_pct = round(actual / budget * 100, 1)
        if usage_pct > 110:
            return "OVER", usage_pct
        elif usage_pct > 90:
            return "WARNING", usage_pct
        else:
            return "OK", usage_pct

    tests = [
        (900000, 1000000, "OK",      90.0),
        (950000, 1000000, "WARNING", 95.0),
        (1150000,1000000, "OVER",    115.0),
        (0,       0,      "NO_BUDGET", 0.0),
    ]
    for actual, budget, exp_status, exp_pct in tests:
        status, pct = calc_budget_status(actual, budget)
        assert status == exp_status, f"상태 오류: actual={actual}, budget={budget}, got={status}"
        assert pct == exp_pct, f"사용률 오류: {pct} (기대: {exp_pct})"
    print(f"  OK  예산 상태: OK/WARNING/OVER/NO_BUDGET 모두 정확")

    # variance 계산
    actual = 1150000
    budget = 1000000
    variance = round(actual - budget, 2)
    assert variance == 150000, f"variance 오류: {variance}"
    remaining = round(budget - actual, 2)
    assert remaining == -150000, f"remaining 오류: {remaining}"
    print(f"  OK  variance={variance:,}원, remaining={remaining:,}원 (초과)")

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

print("\n=== 7. 이상 탐지 로직 검증 ===")
try:
    def detect_anomalies(curr_amt, prev_amt, threshold=30.0):
        if prev_amt <= 0:
            return None
        change_pct = round((curr_amt - prev_amt) / prev_amt * 100, 1)
        if abs(change_pct) >= threshold:
            return {
                "change_pct": change_pct,
                "direction": "UP" if change_pct > 0 else "DOWN",
                "severity": "CRITICAL" if abs(change_pct) >= 50 else "WARNING",
            }
        return None

    # 50% 급증 → CRITICAL
    anomaly = detect_anomalies(1500000, 1000000, 30.0)
    assert anomaly is not None, "50% 급증 탐지 실패"
    assert anomaly["severity"] == "CRITICAL", f"CRITICAL 기대: {anomaly['severity']}"
    assert anomaly["direction"] == "UP", "방향 오류"
    print(f"  OK  50% 급증 탐지: {anomaly['change_pct']}% CRITICAL")

    # 35% 증가 → WARNING
    anomaly2 = detect_anomalies(1350000, 1000000, 30.0)
    assert anomaly2 is not None, "35% 증가 탐지 실패"
    assert anomaly2["severity"] == "WARNING", f"WARNING 기대: {anomaly2['severity']}"
    print(f"  OK  35% 증가 탐지: {anomaly2['change_pct']}% WARNING")

    # 10% 변동 → 탐지 안 함
    anomaly3 = detect_anomalies(1100000, 1000000, 30.0)
    assert anomaly3 is None, "10% 변동 오탐"
    print(f"  OK  10% 변동 미탐지 (정상 범위)")

    # 급감도 탐지
    anomaly4 = detect_anomalies(400000, 1000000, 30.0)
    assert anomaly4 is not None
    assert anomaly4["direction"] == "DOWN", "급감 방향 오류"
    print(f"  OK  60% 급감 탐지: {anomaly4['change_pct']}% DOWN")

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

print("\n=== 8. 비용 트렌드 방향 판정 검증 ===")
try:
    def trend_direction(first_amt, last_amt):
        if first_amt <= 0:
            return "STABLE"
        trend_pct = round((last_amt - first_amt) / first_amt * 100, 1)
        return "UP" if trend_pct > 5 else "DOWN" if trend_pct < -5 else "STABLE"

    assert trend_direction(1000000, 1200000) == "UP",     "20% 증가 UP 기대"
    assert trend_direction(1000000, 800000)  == "DOWN",   "20% 감소 DOWN 기대"
    assert trend_direction(1000000, 1030000) == "STABLE", "3% 변화 STABLE 기대"
    assert trend_direction(0, 1000000)       == "STABLE", "첫달 0 STABLE 기대"
    print(f"  OK  트렌드 방향 판정 UP/DOWN/STABLE 모두 정확")
except AssertionError as e:
    print(f"  ERR  {e}")
    ok = False

print("\n=== 9. 비용 ID 포맷 검증 ===")
try:
    import re as re_mod
    from datetime import datetime as dt

    now = dt.utcnow()
    fake_id = f"COST-{now.strftime('%Y%m%d')}-ABCDEF"
    pattern = re_mod.compile(r"COST-\d{8}-[A-Z0-9]{6}")
    assert pattern.match(fake_id), f"ID 포맷 불일치: {fake_id}"
    assert fake_id.startswith("COST-"), "접두사 오류"
    print(f"  OK  비용 ID 포맷: {fake_id}")
except AssertionError as e:
    print(f"  ERR  {e}")
    ok = False

print("\n=== 10. 보안 정책 확인 ===")
sec_checks = [
    ("localhost:11434" in finops_src,               "Ollama 내부 LLM 사용"),
    ("openai" not in finops_src.lower(),             "외부 OpenAI 미사용"),
    ("anthropic" not in finops_src.lower(),          "외부 Anthropic 미사용"),
    ("ip_addr" not in finops_src,                    "IP 원본 미포함"),
    ("os_pw" not in finops_src,                      "서버 자격증명 미포함"),
    ("disclaimer" in finops_src,                     "AI 분석 면책 조항 포함"),
    ("UserRole.ADMIN" in finops_src,                 "ADMIN 전용 기능 존재"),
]
for check, desc in sec_checks:
    status = "OK" if check else "ERR"
    if status == "ERR": ok = False
    print(f"  {status}  {desc}")

print("\n=== E-5 FinOps 비용 분석 테스트 완료 ===")
if ok:
    print("모든 검사 통과")
else:
    sys.exit(1)
