"""
위험 관리 API — 확률×영향 매트릭스, 자동 이슈 전환.

엔드포인트:
  GET    /api/si/projects/{pid}/risks         — 위험 목록 (레벨 필터)
  POST   /api/si/projects/{pid}/risks         — 위험 등록
  GET    /api/si/projects/{pid}/risks/{id}    — 위험 상세
  PATCH  /api/si/projects/{pid}/risks/{id}    — 위험 수정
  DELETE /api/si/projects/{pid}/risks/{id}    — 위험 삭제
  POST   /api/si/projects/{pid}/risks/{id}/convert-to-issue — 위험 → 이슈 전환
  GET    /api/si/projects/{pid}/risks/matrix  — 위험 매트릭스 (3×3 히트맵용)

위험 점수: probability_level × impact_level (1~9)
  9       → CRITICAL
  5~8     → HIGH (HIGH×HIGH=9 시 자동 이슈 전환 알림)
  3~4     → MEDIUM
  1~2     → LOW
"""
from __future__ import annotations

import logging
from datetime import datetime
from typing import List, Optional
from uuid import uuid4

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

from core.auth import get_current_user
from database import get_db
from models import (
    SiProject, ProjectRisk, ProjectIssue,
    ProjectRiskCreate, ProjectRiskOut, ProjectRiskUpdate,
    IssueType, IssueStatus, RiskLevel, RiskStatus,
    User, UserRole,
)

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/si/projects", tags=["si-risks"])


def _new_risk_id() -> str:
    return f"RSK-{datetime.now().strftime('%Y%m%d')}-{str(uuid4())[:5].upper()}"


def _new_issue_id() -> str:
    return f"ISS-{datetime.now().strftime('%Y%m%d')}-{str(uuid4())[:5].upper()}"


def _calc_risk_level(score: int) -> str:
    if score >= 9:
        return RiskLevel.CRITICAL
    if score >= 5:
        return RiskLevel.HIGH
    if score >= 3:
        return RiskLevel.MEDIUM
    return RiskLevel.LOW


# ── 목록 ──────────────────────────────────────────────────────────────────────

@router.get("/{project_id}/risks", response_model=List[ProjectRiskOut])
async def list_risks(
    project_id: int,
    risk_level: Optional[str] = Query(None),
    status:     Optional[str] = Query(None),
    category:   Optional[str] = Query(None),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    await _assert_project(project_id, db)
    q = select(ProjectRisk).where(ProjectRisk.project_id == project_id)

    if risk_level:
        q = q.where(ProjectRisk.risk_level == risk_level)
    if status:
        q = q.where(ProjectRisk.status == status)
    if category:
        q = q.where(ProjectRisk.category == category)

    q = q.order_by(desc(ProjectRisk.risk_score))
    return (await db.execute(q)).scalars().all()


# ── 생성 ──────────────────────────────────────────────────────────────────────

@router.post("/{project_id}/risks", response_model=ProjectRiskOut, status_code=201)
async def create_risk(
    project_id: int,
    body: ProjectRiskCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    await _assert_project(project_id, db)

    score = body.probability_level * body.impact_level
    level = _calc_risk_level(score)

    risk = ProjectRisk(
        risk_id           = _new_risk_id(),
        project_id        = project_id,
        title             = body.title,
        description       = body.description,
        category          = body.category,
        probability_level = body.probability_level,
        impact_level      = body.impact_level,
        risk_score        = score,
        risk_level        = level,
        mitigation_plan   = body.mitigation_plan,
        contingency_plan  = body.contingency_plan,
        owner             = body.owner,
        due_date          = body.due_date,
    )
    db.add(risk)
    await db.commit()
    await db.refresh(risk)

    # CRITICAL 위험 자동 알림
    if level in (RiskLevel.CRITICAL, RiskLevel.HIGH):
        logger.warning(
            "고위험 등록: project_id=%d risk=%s level=%s score=%d",
            project_id, risk.risk_id, level, score,
        )

    return risk


# ── 상세 ──────────────────────────────────────────────────────────────────────

@router.get("/{project_id}/risks/{risk_id}", response_model=ProjectRiskOut)
async def get_risk(
    project_id: int, risk_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    return await _get_risk_or_404(project_id, risk_id, db)


# ── 수정 ──────────────────────────────────────────────────────────────────────

@router.patch("/{project_id}/risks/{risk_id}", response_model=ProjectRiskOut)
async def update_risk(
    project_id: int, risk_id: int,
    body: ProjectRiskUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    risk = await _get_risk_or_404(project_id, risk_id, db)

    data = body.model_dump(exclude_none=True)
    for field, val in data.items():
        setattr(risk, field, val)

    # 확률/영향 변경 시 점수 재계산
    if "probability_level" in data or "impact_level" in data:
        risk.risk_score = risk.probability_level * risk.impact_level
        risk.risk_level = _calc_risk_level(risk.risk_score)

    await db.commit()
    await db.refresh(risk)
    return risk


# ── 위험 → 이슈 전환 ──────────────────────────────────────────────────────────

@router.post("/{project_id}/risks/{risk_id}/convert-to-issue")
async def convert_risk_to_issue(
    project_id: int, risk_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """위험이 실제 발생했을 때 이슈로 전환."""
    if current_user.role not in (UserRole.ADMIN, UserRole.PM):
        raise HTTPException(403, "PM 이상 권한 필요")
    risk = await _get_risk_or_404(project_id, risk_id, db)

    if risk.converted_to_issue:
        raise HTTPException(400, "이미 이슈로 전환된 위험입니다")

    issue = ProjectIssue(
        issue_id    = _new_issue_id(),
        project_id  = project_id,
        issue_type  = IssueType.OTHER,
        title       = f"[위험 전환] {risk.title}",
        description = risk.description,
        priority    = "HIGH" if risk.risk_level in ("HIGH", "CRITICAL") else "MEDIUM",
        raised_by   = current_user.username,
        impact      = f"위험 점수: {risk.risk_score} ({risk.risk_level})\n완화 계획: {risk.mitigation_plan or '없음'}",
        from_risk_id = risk.id,
    )
    db.add(issue)

    risk.converted_to_issue = True
    risk.status = RiskStatus.OPEN

    await db.commit()
    await db.refresh(issue)
    return {"message": "위험이 이슈로 전환되었습니다", "issue_id": issue.issue_id}


# ── 삭제 ──────────────────────────────────────────────────────────────────────

@router.delete("/{project_id}/risks/{risk_id}", status_code=204)
async def delete_risk(
    project_id: int, risk_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role not in (UserRole.ADMIN, UserRole.PM):
        raise HTTPException(403, "PM 이상 권한 필요")
    risk = await _get_risk_or_404(project_id, risk_id, db)
    await db.delete(risk)
    await db.commit()


# ── 위험 매트릭스 ─────────────────────────────────────────────────────────────

@router.get("/{project_id}/risks/matrix")
async def get_risk_matrix(
    project_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """3×3 위험 매트릭스 히트맵 데이터. 셀별 위험 목록 반환."""
    await _assert_project(project_id, db)
    risks = (await db.execute(
        select(ProjectRisk).where(
            ProjectRisk.project_id == project_id,
            ProjectRisk.status == RiskStatus.OPEN,
        )
    )).scalars().all()

    # 3×3 매트릭스 초기화 (probability × impact)
    matrix: dict = {}
    for p in (1, 2, 3):
        for i in (1, 2, 3):
            matrix[f"{p}x{i}"] = {
                "probability": p, "impact": i,
                "score": p * i,
                "level": _calc_risk_level(p * i),
                "risks": [],
            }

    for r in risks:
        key = f"{r.probability_level}x{r.impact_level}"
        if key in matrix:
            matrix[key]["risks"].append({
                "id":      r.id,
                "risk_id": r.risk_id,
                "title":   r.title,
                "owner":   r.owner,
            })

    return {
        "project_id": project_id,
        "open_risks": len(risks),
        "matrix": list(matrix.values()),
    }


# ── 내부 헬퍼 ─────────────────────────────────────────────────────────────────

async def _assert_project(project_id: int, db: AsyncSession) -> None:
    if not (await db.execute(
        select(SiProject).where(SiProject.id == project_id)
    )).scalars().first():
        raise HTTPException(404, f"SI 프로젝트 {project_id}를 찾을 수 없습니다")


async def _get_risk_or_404(project_id: int, risk_id: int, db: AsyncSession) -> ProjectRisk:
    risk = (await db.execute(
        select(ProjectRisk).where(
            ProjectRisk.id == risk_id,
            ProjectRisk.project_id == project_id,
        )
    )).scalars().first()
    if not risk:
        raise HTTPException(404, "위험을 찾을 수 없습니다")
    return risk
