"""
공급망 보안 (Supply Chain Security)

엔드포인트:
  GET  /api/supply-chain/scan                     — 공급망 스캔 현황
  POST /api/supply-chain/scan                     — 전체 공급망 스캔 실행
  GET  /api/supply-chain/vulnerabilities          — 취약점 목록 (심각도별)
  POST /api/supply-chain/vulnerabilities/{id}/patch — 취약점 패치 요청 (SR 생성)
  GET  /api/supply-chain/dependencies             — 의존성 + CVE 상태
  GET  /api/supply-chain/slsa-level               — SLSA 레벨 평가 (0~3)
  GET  /api/supply-chain/pipeline-integrity       — 파이프라인 무결성
  GET  /api/supply-chain/report                   — 공급망 보안 리포트
"""
from __future__ import annotations

import json
import logging
from datetime import datetime
from typing import List, Optional

from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import desc, select
from sqlalchemy.ext.asyncio import AsyncSession

from core.auth import get_current_user
from database import get_db, SessionLocal
from models import (
    SCSScan,
    SLSAAssessment,
    SRRequest,
    SRStatus,
    SRType,
    SupplyChainVulnerability,
    User,
    UserRole,
)

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/supply-chain", tags=["supply_chain_security"])


# ── 알려진 취약 패키지 내장 데이터베이스 ─────────────────────────────────────────

KNOWN_VULNERABILITIES: List[dict] = [
    {
        "package": "log4j",
        "versions": ["<2.17.0"],
        "cve": "CVE-2021-44228",
        "severity": "CRITICAL",
        "cvss": 10.0,
        "description": "Apache Log4j2 원격 코드 실행 취약점 (Log4Shell)",
        "fixed_version": "2.17.0",
    },
    {
        "package": "spring-core",
        "versions": ["<5.3.18"],
        "cve": "CVE-2022-22965",
        "severity": "CRITICAL",
        "cvss": 9.8,
        "description": "Spring Framework RCE 취약점 (Spring4Shell)",
        "fixed_version": "5.3.18",
    },
    {
        "package": "requests",
        "versions": ["<2.31.0"],
        "cve": "CVE-2023-32681",
        "severity": "MEDIUM",
        "cvss": 6.1,
        "description": "requests 라이브러리 Proxy-Authorization 헤더 노출",
        "fixed_version": "2.31.0",
    },
    {
        "package": "pillow",
        "versions": ["<10.0.0"],
        "cve": "CVE-2023-44271",
        "severity": "HIGH",
        "cvss": 7.5,
        "description": "Pillow 이미지 처리 DoS 취약점",
        "fixed_version": "10.0.0",
    },
    {
        "package": "openssl",
        "versions": ["<3.0.7"],
        "cve": "CVE-2022-3786",
        "severity": "HIGH",
        "cvss": 7.5,
        "description": "OpenSSL 버퍼 오버플로우 취약점",
        "fixed_version": "3.0.7",
    },
    {
        "package": "django",
        "versions": ["<4.2.7"],
        "cve": "CVE-2023-43665",
        "severity": "HIGH",
        "cvss": 7.5,
        "description": "Django Denial of Service 취약점",
        "fixed_version": "4.2.7",
    },
    {
        "package": "fastapi",
        "versions": ["<0.109.1"],
        "cve": "CVE-2024-24762",
        "severity": "HIGH",
        "cvss": 7.5,
        "description": "FastAPI ReDoS 취약점 (multipart form data)",
        "fixed_version": "0.109.1",
    },
    {
        "package": "cryptography",
        "versions": ["<41.0.6"],
        "cve": "CVE-2023-49083",
        "severity": "MEDIUM",
        "cvss": 4.0,
        "description": "Python cryptography NULL 포인터 역참조",
        "fixed_version": "41.0.6",
    },
]

# ── SLSA 레벨 정의 ──────────────────────────────────────────────────────────

SLSA_REQUIREMENTS = {
    0: {
        "name": "SLSA Level 0 — 기준 없음",
        "description": "SLSA 요구사항 미충족. 기본 빌드 프로세스만 존재.",
        "requirements": ["기본 소스 코드 존재"],
        "guardia_checks": [],
    },
    1: {
        "name": "SLSA Level 1 — 빌드 스크립트 정의",
        "description": "빌드 프로세스가 스크립트로 정의되어 있어야 함.",
        "requirements": [
            "빌드 스크립트 존재 (Jenkinsfile, Makefile 등)",
            "빌드 결과물 생성 기록",
        ],
        "guardia_checks": ["Jenkinsfile 존재 여부 확인"],
    },
    2: {
        "name": "SLSA Level 2 — 버전 관리 + CI 서비스",
        "description": "버전 관리 시스템과 CI 서비스를 통한 빌드가 필요.",
        "requirements": [
            "소스 버전 관리 (Git)",
            "CI 서비스 사용 (Jenkins)",
            "빌드 출처 메타데이터 생성",
        ],
        "guardia_checks": [
            "Gitea 저장소 연결",
            "Jenkins 빌드 이력",
            "빌드 아티팩트 해시 기록",
        ],
    },
    3: {
        "name": "SLSA Level 3 — 검증 가능한 빌드 출처",
        "description": "서명된 빌드 출처(provenance)가 포함된 아티팩트 배포.",
        "requirements": [
            "서명된 아티팩트 (코드 서명)",
            "빌드 환경 격리",
            "외부 검증 가능한 빌드 출처 문서",
            "재현 가능한 빌드(Reproducible Build)",
        ],
        "guardia_checks": [
            "아티팩트 서명 검증",
            "빌드 환경 컨테이너 격리",
            "SLSA Provenance 문서 생성",
        ],
    },
}


# ── 버전 비교 헬퍼 ──────────────────────────────────────────────────────────

def _version_lt(ver: str, threshold: str) -> bool:
    """단순 버전 비교: ver < threshold 여부 반환."""
    try:
        def _parse(v: str):
            return tuple(int(x) for x in v.strip().lstrip("v").split(".")[:4])
        return _parse(ver) < _parse(threshold)
    except Exception:
        return False


def _check_package_vuln(pkg_name: str, pkg_version: str) -> Optional[dict]:
    """패키지명·버전을 KNOWN_VULNERABILITIES와 대조하여 매칭 항목 반환."""
    pkg_lower = pkg_name.lower()
    for vuln in KNOWN_VULNERABILITIES:
        if vuln["package"].lower() not in pkg_lower:
            continue
        for ver_constraint in vuln["versions"]:
            if ver_constraint.startswith("<"):
                threshold = ver_constraint[1:].strip()
                if _version_lt(pkg_version, threshold):
                    return vuln
    return None


# ── 샘플 의존성 파싱 (에이전트리스 SSH 스텁) ─────────────────────────────────

async def _parse_dependencies_sample() -> List[dict]:
    """
    실제 구현 시: paramiko SSH로 requirements.txt / package.json 파싱.
    현재는 현실적인 샘플 데이터를 반환한다.
    """
    return [
        {"name": "fastapi",     "version": "0.100.0", "ecosystem": "pypi"},
        {"name": "sqlalchemy",  "version": "2.0.15",  "ecosystem": "pypi"},
        {"name": "requests",    "version": "2.28.0",  "ecosystem": "pypi"},
        {"name": "pillow",      "version": "9.5.0",   "ecosystem": "pypi"},
        {"name": "cryptography","version": "41.0.3",  "ecosystem": "pypi"},
        {"name": "pydantic",    "version": "2.5.0",   "ecosystem": "pypi"},
        {"name": "uvicorn",     "version": "0.24.0",  "ecosystem": "pypi"},
        {"name": "nginx",       "version": "1.24.0",  "ecosystem": "system"},
        {"name": "openssl",     "version": "3.0.2",   "ecosystem": "system"},
        {"name": "django",      "version": "4.1.13",  "ecosystem": "pypi"},
    ]


# ── 백그라운드 스캔 실행 ─────────────────────────────────────────────────────

async def _run_supply_chain_scan(scan_id: int) -> None:
    """전체 공급망 스캔: 의존성 파싱 → CVE 매핑 → DB 저장."""
    async with SessionLocal() as db:
        row = await db.execute(select(SCSScan).where(SCSScan.id == scan_id))
        scan = row.scalar_one_or_none()
        if not scan:
            return

        try:
            scan.status = "running"
            await db.commit()

            deps = await _parse_dependencies_sample()
            found_vulns = []
            critical = 0
            high = 0

            for dep in deps:
                match = _check_package_vuln(dep["name"], dep["version"])
                if match:
                    found_vulns.append({
                        "package": dep["name"],
                        "version": dep["version"],
                        "cve":     match["cve"],
                        "severity": match["severity"],
                        "cvss":    match["cvss"],
                        "fixed_version": match["fixed_version"],
                        "description":   match["description"],
                    })
                    if match["severity"] == "CRITICAL":
                        critical += 1
                    elif match["severity"] == "HIGH":
                        high += 1

                    # 취약점 레코드 upsert
                    existing = (await db.execute(
                        select(SupplyChainVulnerability).where(
                            SupplyChainVulnerability.cve_id == match["cve"],
                            SupplyChainVulnerability.package == dep["name"],
                        )
                    )).scalar_one_or_none()

                    if not existing:
                        db.add(SupplyChainVulnerability(
                            cve_id          = match["cve"],
                            package         = dep["name"],
                            version         = dep["version"],
                            fixed_version   = match["fixed_version"],
                            severity        = match["severity"],
                            cvss_score      = match["cvss"],
                            description     = match["description"],
                            patch_available = True,
                            status          = "open",
                        ))

            scan.status         = "completed"
            scan.findings_count = len(found_vulns)
            scan.critical_count = critical
            scan.high_count     = high
            scan.report         = json.dumps(found_vulns, ensure_ascii=False)
            await db.commit()

        except Exception as exc:
            scan.status = "failed"
            scan.report = json.dumps({"error": str(exc)[:200]})
            await db.commit()
            logger.error("공급망 스캔 실패 (scan_id=%d): %s", scan_id, exc)


# ── SLSA 평가 헬퍼 ──────────────────────────────────────────────────────────

def _evaluate_slsa_level() -> dict:
    """
    GUARDiA 환경 기준 SLSA 레벨 평가.
    Gitea + Jenkins 운영 중 → Level 2 달성 가능.
    """
    from pathlib import Path

    achieved = 0
    gaps: List[str] = []
    details: dict = {}

    # Level 1: Jenkinsfile 존재 여부
    jenkinsfile_paths = [
        Path("C:/GUARDiA/workspace/guardia-itsm/Jenkinsfile"),
        Path("C:/GUARDiA/repos/guardia-itsm/Jenkinsfile"),
    ]
    has_jenkinsfile = any(p.exists() for p in jenkinsfile_paths)
    details["level1_jenkinsfile"] = has_jenkinsfile
    if not has_jenkinsfile:
        gaps.append("Jenkinsfile 미존재 — CI 빌드 스크립트 정의 필요")

    # Level 2: Gitea 저장소 연결 + Jenkins 가용성
    gitea_repo_exists = Path("C:/GUARDiA/repos/guardia-itsm/.git").exists()
    details["level2_gitea_repo"] = gitea_repo_exists
    if not gitea_repo_exists:
        gaps.append("Gitea 저장소 미연결")

    # Level 2 달성 조건: Jenkinsfile + Gitea repo
    if has_jenkinsfile and gitea_repo_exists:
        achieved = 2
    elif has_jenkinsfile or gitea_repo_exists:
        achieved = 1
    else:
        achieved = 0

    # Level 3: 서명된 아티팩트 — 현재 미구현
    details["level3_signed_artifacts"] = False
    gaps.append("서명된 아티팩트 미구현 — Level 3 달성을 위해 코드 서명 도구 도입 필요")

    score = (achieved / 3) * 100.0
    return {
        "level":       achieved,
        "score":       round(score, 1),
        "gaps":        gaps,
        "details":     details,
        "definition":  SLSA_REQUIREMENTS[achieved],
    }


# ── Pydantic 스키마 ──────────────────────────────────────────────────────────

class PatchRequestIn(BaseModel):
    note: Optional[str] = None


# ── 엔드포인트 ───────────────────────────────────────────────────────────────

@router.get("/scan")
async def get_scan_status(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """공급망 스캔 현황 — 최근 20건."""
    rows = await db.execute(
        select(SCSScan).order_by(desc(SCSScan.created_at)).limit(20)
    )
    scans = rows.scalars().all()

    latest = scans[0] if scans else None
    summary = {
        "total_scans":    len(scans),
        "last_scan_at":   latest.created_at.isoformat() if latest else None,
        "last_status":    latest.status if latest else "none",
        "last_findings":  latest.findings_count if latest else 0,
        "last_critical":  latest.critical_count if latest else 0,
        "last_high":      latest.high_count if latest else 0,
    }

    return {
        "summary": summary,
        "scans": [
            {
                "id":             s.id,
                "scan_type":      s.scan_type,
                "target":         s.target,
                "status":         s.status,
                "findings_count": s.findings_count,
                "critical_count": s.critical_count,
                "high_count":     s.high_count,
                "created_at":     s.created_at.isoformat(),
            }
            for s in scans
        ],
    }


@router.post("/scan", status_code=202)
async def run_supply_chain_scan(
    background_tasks: BackgroundTasks,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """전체 공급망 스캔 실행 (비동기, 202 Accepted)."""
    if current_user.role not in (UserRole.ADMIN, UserRole.PM):
        raise HTTPException(403, "PM/ADMIN 권한이 필요합니다.")

    scan = SCSScan(
        scan_type="dependency",
        target="guardia-itsm/requirements.txt",
        status="queued",
    )
    db.add(scan)
    await db.commit()
    await db.refresh(scan)

    background_tasks.add_task(_run_supply_chain_scan, scan.id)

    logger.info("공급망 스캔 시작 (scan_id=%d, by=%s)", scan.id, current_user.username)
    return {
        "scan_id": scan.id,
        "status":  "queued",
        "message": "공급망 스캔이 시작되었습니다. GET /api/supply-chain/scan 으로 결과를 확인하세요.",
    }


@router.get("/vulnerabilities")
async def list_vulnerabilities(
    severity:  Optional[str] = Query(None, description="CRITICAL|HIGH|MEDIUM|LOW"),
    status:    Optional[str] = Query(None, description="open|patched|accepted"),
    limit:     int = Query(50, ge=1, le=200),
    offset:    int = Query(0, ge=0),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """취약점 목록 조회 (심각도별 필터 지원)."""
    query = select(SupplyChainVulnerability).order_by(
        desc(SupplyChainVulnerability.cvss_score)
    )

    if severity:
        query = query.where(
            SupplyChainVulnerability.severity == severity.upper()
        )
    if status:
        query = query.where(
            SupplyChainVulnerability.status == status.lower()
        )

    total_rows = await db.execute(query)
    all_vulns  = total_rows.scalars().all()

    # 심각도 집계
    severity_counts: dict = {}
    for v in all_vulns:
        sev = v.severity or "UNKNOWN"
        severity_counts[sev] = severity_counts.get(sev, 0) + 1

    paged = all_vulns[offset: offset + limit]

    return {
        "total":            len(all_vulns),
        "severity_summary": severity_counts,
        "vulnerabilities": [
            {
                "id":              v.id,
                "cve_id":          v.cve_id,
                "package":         v.package,
                "version":         v.version,
                "fixed_version":   v.fixed_version,
                "severity":        v.severity,
                "cvss_score":      v.cvss_score,
                "description":     v.description,
                "patch_available": v.patch_available,
                "status":          v.status,
                "created_at":      v.created_at.isoformat(),
            }
            for v in paged
        ],
    }


@router.post("/vulnerabilities/{vuln_id}/patch", status_code=201)
async def request_patch_sr(
    vuln_id: int,
    body: PatchRequestIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """
    취약점 패치 요청 — SR(서비스 요청) 자동 생성.
    보안: IP, 비밀번호, SSH 계정은 SR 내용에 절대 포함하지 않는다.
    """
    row = await db.execute(
        select(SupplyChainVulnerability).where(
            SupplyChainVulnerability.id == vuln_id
        )
    )
    vuln = row.scalar_one_or_none()
    if not vuln:
        raise HTTPException(404, f"취약점 ID {vuln_id}를 찾을 수 없습니다.")

    if vuln.status == "patched":
        raise HTTPException(400, "이미 패치 완료된 취약점입니다.")

    # SR 내용 구성 — 서버 자격증명 절대 미포함
    sr_title = (
        f"[공급망 보안] {vuln.package} 취약점 패치 요청 ({vuln.cve_id or '미분류'})"
    )
    sr_description = (
        f"패키지: {vuln.package} v{vuln.version or '미상'}\n"
        f"취약점: {vuln.cve_id or '미분류'} (CVSS {vuln.cvss_score:.1f} / {vuln.severity})\n"
        f"설명: {vuln.description or '-'}\n"
        f"권고 버전: {vuln.fixed_version or '최신 버전으로 업그레이드'}\n"
        f"추가 요청 사항: {body.note or '-'}"
    )

    import hashlib as _hs
    _ts  = datetime.utcnow().strftime("%Y%m%d%H%M%S")
    _uid = _hs.sha256(f"scs-{vuln_id}-{_ts}".encode()).hexdigest()[:8].upper()
    sr_id_str = f"SCS-{_ts[:8]}-{_uid}"

    sr = SRRequest(
        sr_id        = sr_id_str,
        title        = sr_title,
        description  = sr_description,
        status       = SRStatus.RECEIVED,
        sr_type      = SRType.OTHER,
        requested_by = current_user.username,
    )
    db.add(sr)

    # 취약점 상태를 'open' → 패치 요청 접수로 표시
    vuln.status = "open"  # SR 생성 후에도 open 유지 — 실제 패치 완료 시 patched 처리
    await db.commit()
    await db.refresh(sr)

    logger.info(
        "공급망 취약점 패치 SR 생성: vuln_id=%d cve=%s sr_id=%d by=%s",
        vuln_id, vuln.cve_id, sr.id, current_user.username,
    )

    return {
        "message":   f"패치 요청 SR이 생성되었습니다. (SR #{sr.id})",
        "sr_id":     sr.id,
        "sr_title":  sr.title,
        "vuln_id":   vuln_id,
        "cve_id":    vuln.cve_id,
        "severity":  vuln.severity,
    }


@router.get("/dependencies")
async def list_dependencies(
    current_user: User = Depends(get_current_user),
):
    """
    의존성 목록 + 각 패키지의 CVE 상태 반환.
    에이전트리스: requirements.txt 샘플 파싱.
    """
    deps = await _parse_dependencies_sample()
    result = []

    for dep in deps:
        match = _check_package_vuln(dep["name"], dep["version"])
        result.append({
            "name":       dep["name"],
            "version":    dep["version"],
            "ecosystem":  dep["ecosystem"],
            "vulnerable": match is not None,
            "cve_id":     match["cve"]        if match else None,
            "severity":   match["severity"]   if match else None,
            "cvss_score": match["cvss"]        if match else None,
            "fixed_version": match["fixed_version"] if match else None,
        })

    vulnerable_count = sum(1 for d in result if d["vulnerable"])
    return {
        "total_dependencies": len(result),
        "vulnerable_count":   vulnerable_count,
        "safe_count":         len(result) - vulnerable_count,
        "dependencies":       result,
    }


@router.get("/slsa-level")
async def get_slsa_level(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """
    SLSA 레벨 평가 (0~3).
    GUARDiA는 Gitea + Jenkins 운영 → Level 2 달성 가능.
    """
    evaluation = _evaluate_slsa_level()

    # DB에 평가 이력 저장
    assessment = SLSAAssessment(
        level        = evaluation["level"],
        score        = evaluation["score"],
        requirements = json.dumps(
            evaluation["definition"]["requirements"], ensure_ascii=False
        ),
        gaps         = json.dumps(evaluation["gaps"], ensure_ascii=False),
    )
    db.add(assessment)
    await db.commit()

    return {
        "current_level":   evaluation["level"],
        "level_name":      evaluation["definition"]["name"],
        "score_pct":       evaluation["score"],
        "description":     evaluation["definition"]["description"],
        "achieved_checks": evaluation["details"],
        "gaps":            evaluation["gaps"],
        "level_definitions": {
            str(k): {
                "name":         v["name"],
                "description":  v["description"],
                "requirements": v["requirements"],
            }
            for k, v in SLSA_REQUIREMENTS.items()
        },
        "recommendation": (
            "Level 3 달성을 위해 아티팩트 서명(코드 서명) 및 "
            "재현 가능한 빌드 환경 구축이 필요합니다."
            if evaluation["level"] < 3 else
            "SLSA Level 3 달성 완료. 정기 감사를 유지하세요."
        ),
    }


@router.get("/pipeline-integrity")
async def get_pipeline_integrity(
    current_user: User = Depends(get_current_user),
):
    """
    CI/CD 파이프라인 무결성 점검.
    Jenkinsfile, Gitea 저장소, 배포 스크립트 존재 여부 확인.
    """
    from pathlib import Path

    checks = []

    # 1. Jenkinsfile 존재 여부
    jenkinsfile_paths = [
        ("guardia-itsm Jenkinsfile",    "C:/GUARDiA/workspace/guardia-itsm/Jenkinsfile"),
        ("guardia-manager Jenkinsfile", "C:/GUARDiA/workspace/guardia-manager/Jenkinsfile"),
    ]
    for label, path in jenkinsfile_paths:
        exists = Path(path).exists()
        checks.append({
            "check":  label,
            "status": "pass" if exists else "fail",
            "detail": f"{path} {'존재' if exists else '미존재'}",
        })

    # 2. Gitea repo .git 존재
    repo_paths = [
        ("guardia-itsm Gitea repo",    "C:/GUARDiA/repos/guardia-itsm/.git"),
        ("guardia-manager Gitea repo", "C:/GUARDiA/repos/guardia-manager/.git"),
    ]
    for label, path in repo_paths:
        exists = Path(path).exists()
        checks.append({
            "check":  label,
            "status": "pass" if exists else "warn",
            "detail": f"{path} {'연결됨' if exists else '미연결'}",
        })

    # 3. deploy_server.py (webhook 수신기)
    deploy_server = Path("C:/GUARDiA/scripts/deploy/deploy_server.py")
    checks.append({
        "check":  "Webhook 배포 수신기",
        "status": "pass" if deploy_server.exists() else "fail",
        "detail": str(deploy_server),
    })

    # 4. requirements.txt 잠금 파일
    req_file = Path("C:/GUARDiA/workspace/guardia-itsm/requirements.txt")
    checks.append({
        "check":  "requirements.txt 의존성 잠금",
        "status": "pass" if req_file.exists() else "warn",
        "detail": str(req_file),
    })

    pass_count = sum(1 for c in checks if c["status"] == "pass")
    fail_count = sum(1 for c in checks if c["status"] == "fail")
    warn_count = sum(1 for c in checks if c["status"] == "warn")

    overall = (
        "healthy"   if fail_count == 0 and warn_count == 0 else
        "degraded"  if fail_count == 0 else
        "critical"
    )

    return {
        "overall_status":  overall,
        "pass_count":      pass_count,
        "fail_count":      fail_count,
        "warn_count":      warn_count,
        "integrity_score": round(pass_count / len(checks) * 100, 1),
        "checks":          checks,
        "checked_at":      datetime.utcnow().isoformat(),
    }


@router.get("/report")
async def get_supply_chain_report(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """
    공급망 보안 종합 리포트:
    스캔 이력, 취약점 통계, SLSA 레벨, 파이프라인 무결성 요약.
    """
    # 최신 스캔
    scan_row = await db.execute(
        select(SCSScan).order_by(desc(SCSScan.created_at)).limit(1)
    )
    latest_scan = scan_row.scalar_one_or_none()

    # 취약점 통계
    vuln_rows = await db.execute(select(SupplyChainVulnerability))
    all_vulns = vuln_rows.scalars().all()
    open_vulns   = [v for v in all_vulns if v.status == "open"]
    patched_vulns = [v for v in all_vulns if v.status == "patched"]

    sev_summary: dict = {}
    for v in open_vulns:
        sev = v.severity or "UNKNOWN"
        sev_summary[sev] = sev_summary.get(sev, 0) + 1

    # SLSA 평가
    slsa = _evaluate_slsa_level()

    # 의존성 취약률
    deps = await _parse_dependencies_sample()
    vuln_dep_count = sum(
        1 for d in deps if _check_package_vuln(d["name"], d["version"])
    )

    # 전체 위험 점수 (간이 계산)
    critical_weight = sev_summary.get("CRITICAL", 0) * 10
    high_weight     = sev_summary.get("HIGH", 0) * 7
    medium_weight   = sev_summary.get("MEDIUM", 0) * 4
    risk_score      = min(100, critical_weight + high_weight + medium_weight)

    risk_label = (
        "CRITICAL" if risk_score >= 70 else
        "HIGH"     if risk_score >= 40 else
        "MEDIUM"   if risk_score >= 20 else
        "LOW"
    )

    return {
        "generated_at":   datetime.utcnow().isoformat(),
        "generated_by":   current_user.username,
        "risk_score":     risk_score,
        "risk_level":     risk_label,
        "scan_summary": {
            "last_scan_at":   latest_scan.created_at.isoformat() if latest_scan else None,
            "last_status":    latest_scan.status if latest_scan else "none",
            "last_findings":  latest_scan.findings_count if latest_scan else 0,
        },
        "vulnerability_summary": {
            "total_open":     len(open_vulns),
            "total_patched":  len(patched_vulns),
            "by_severity":    sev_summary,
            "patch_rate_pct": (
                round(len(patched_vulns) / len(all_vulns) * 100, 1)
                if all_vulns else 0.0
            ),
        },
        "dependency_summary": {
            "total_dependencies": len(deps),
            "vulnerable_count":   vuln_dep_count,
            "vulnerability_rate_pct": round(
                vuln_dep_count / len(deps) * 100, 1
            ) if deps else 0.0,
        },
        "slsa_summary": {
            "current_level": slsa["level"],
            "level_name":    slsa["definition"]["name"],
            "score_pct":     slsa["score"],
            "gaps_count":    len(slsa["gaps"]),
        },
        "recommendations": _build_recommendations(sev_summary, slsa["level"], vuln_dep_count),
    }


def _build_recommendations(
    sev_summary: dict,
    slsa_level: int,
    vuln_dep_count: int,
) -> List[str]:
    """우선순위 개선 권고 사항 생성."""
    recs: List[str] = []

    if sev_summary.get("CRITICAL", 0) > 0:
        recs.append(
            f"[긴급] CRITICAL 취약점 {sev_summary['CRITICAL']}건을 즉시 패치하십시오."
        )
    if sev_summary.get("HIGH", 0) > 0:
        recs.append(
            f"[높음] HIGH 취약점 {sev_summary['HIGH']}건에 대한 패치 SR을 금주 내 생성하십시오."
        )
    if vuln_dep_count > 0:
        recs.append(
            f"의존성 {vuln_dep_count}개에 알려진 취약점이 존재합니다. requirements.txt를 갱신하십시오."
        )
    if slsa_level < 2:
        recs.append(
            "SLSA Level 2 달성을 위해 Gitea 저장소 연결 및 Jenkinsfile 작성이 필요합니다."
        )
    if slsa_level < 3:
        recs.append(
            "SLSA Level 3 달성을 위해 빌드 아티팩트 코드 서명 도구 도입을 검토하십시오."
        )
    if not recs:
        recs.append("현재 공급망 보안 상태가 양호합니다. 정기 스캔을 유지하십시오.")

    return recs
