"""
자율 운영 자동처리 엔진.

위험도(RiskLevel) 기반 자동/승인 분기:
  LOW    → 즉시 자동 처리 + 감사 기록
  MEDIUM → 자동 처리 + 운영팀 알림
  HIGH   → 승인 요청 메시지 발송 후 대기
  CRITICAL → 차단 + 관리자 승인 필수

자동 처리 항목:
  - SR 자동 분류·배정 (키워드/ML)
  - INQUIRY SR → KB 검색 후 자동 응답
  - 헬스체크 이상 → 인시던트 자동 생성
  - SLA 위반 임박 → 자동 에스컬레이션
  - 취약점 스캔 결과 → 보안 SR 자동 생성
  - KB 아티클 자동 초안 생성 (SR/인시던트 완료 후)
  - 배치 실패 → 알림 + 재시도

승인 필요 항목:
  - 서버 재시작 / 서비스 중단
  - 운영(PRD) 환경 배포
  - DR Failover 실행
  - 대량 SR 상태 일괄 변경
  - 사용자 계정 비활성화
  - 보안 정책 변경
"""
from __future__ import annotations

import logging
from datetime import datetime, timedelta
from enum import Enum
from typing import Optional

from sqlalchemy import select, and_, func as sqlfunc
from sqlalchemy.ext.asyncio import AsyncSession

logger = logging.getLogger(__name__)


class RiskLevel(str, Enum):
    LOW      = "LOW"       # 자동 처리
    MEDIUM   = "MEDIUM"    # 자동 처리 + 알림
    HIGH     = "HIGH"      # 승인 필요
    CRITICAL = "CRITICAL"  # 관리자 승인 필수


# ── 위험도 평가 ──────────────────────────────────────────────────────────────

def assess_risk(action_type: str, context: dict) -> RiskLevel:
    """
    작업 유형과 컨텍스트 기반 위험도 평가.
    환경(PRD/STG/DEV), 대상 서버 수, 작업 종류를 고려.
    """
    env         = str(context.get("environment", "")).upper()
    target_count = int(context.get("target_count", 1))
    sr_priority  = str(context.get("priority", "MEDIUM")).upper()

    AUTO_ACTIONS = {
        "sr_classify", "sr_assign", "kb_answer", "kb_draft",
        "health_notify", "sla_escalate", "vuln_notify",
        "batch_retry_notify", "report_generate", "anomaly_notify",
    }
    MEDIUM_ACTIONS = {
        "sr_auto_close", "log_collect", "ssl_notify", "perf_report",
    }
    HIGH_ACTIONS = {
        "server_restart", "service_stop", "deploy_stg",
        "bulk_sr_update", "account_disable", "script_exec",
    }
    CRITICAL_ACTIONS = {
        "deploy_prd", "dr_failover", "db_schema_change",
        "security_policy_change", "bulk_delete", "network_change",
    }

    if action_type in AUTO_ACTIONS:
        return RiskLevel.LOW

    if action_type in MEDIUM_ACTIONS:
        return RiskLevel.MEDIUM

    if action_type in HIGH_ACTIONS:
        # 운영 환경이면 CRITICAL로 격상
        if env == "PRD" or target_count > 5:
            return RiskLevel.CRITICAL
        return RiskLevel.HIGH

    if action_type in CRITICAL_ACTIONS:
        return RiskLevel.CRITICAL

    # 미분류 → 안전하게 HIGH 처리
    return RiskLevel.HIGH


# ── SR 자동 분류 ──────────────────────────────────────────────────────────────

async def auto_classify_sr(db: AsyncSession, sr_id: int) -> dict:
    """
    SR 자동 분류 + 담당자 배정.
    키워드 매핑 → sr_type, priority 갱신, 담당자 자동 배정.
    """
    from models import SRRequest, Priority, SRType
    from routers.assign import auto_assign_engine

    q = await db.execute(select(SRRequest).where(SRRequest.id == sr_id))
    sr = q.scalar_one_or_none()
    if not sr:
        return {"success": False, "error": "SR 없음"}

    text = f"{sr.title} {sr.description or ''}".lower()

    # 타입 추론
    type_map = {
        SRType.DEPLOY:  ["배포", "deploy", "릴리즈", "release", "업데이트", "update"],
        SRType.RESTART: ["재시작", "restart", "재구동", "중단", "stop", "기동", "start"],
        SRType.LOG:     ["로그", "log", "오류", "error", "에러", "확인"],
        SRType.INQUIRY: ["문의", "질문", "어떻게", "방법", "how", "what", "?"],
    }
    inferred_type = SRType.OTHER
    for sr_type, keywords in type_map.items():
        if any(kw in text for kw in keywords):
            inferred_type = sr_type
            break

    # 우선순위 추론
    priority_map = {
        Priority.CRITICAL: ["긴급", "장애", "critical", "emergency", "불가", "서비스 중단"],
        Priority.HIGH:     ["높음", "high", "빠른", "즉시", "soon"],
        Priority.LOW:      ["낮음", "low", "여유", "천천히"],
    }
    inferred_priority = Priority.MEDIUM
    for prio, keywords in priority_map.items():
        if any(kw in text for kw in keywords):
            inferred_priority = prio
            break

    changed = []
    if sr.sr_type != inferred_type:
        sr.sr_type = inferred_type
        changed.append(f"타입: {inferred_type}")
    if sr.priority != inferred_priority:
        sr.priority = inferred_priority
        changed.append(f"우선순위: {inferred_priority}")

    # 담당자 자동 배정
    assigned = await auto_assign_engine(db, sr)
    if assigned:
        changed.append(f"담당자: {sr.assigned_to}")

    await db.commit()
    return {
        "success": True,
        "sr_id": sr.sr_id,
        "changes": changed,
        "auto_action": "sr_classify",
    }


# ── INQUIRY SR → KB 자동 응답 ────────────────────────────────────────────────

async def auto_answer_inquiry(db: AsyncSession, sr_id: int) -> dict:
    """
    문의형(INQUIRY) SR에 KB 검색 결과를 자동 댓글로 답변.
    신뢰도 80% 이상이면 자동 답변 + SR COMPLETED 처리.
    """
    from models import SRRequest, SRStatus, SRType
    from core.kb_agent import search_kb_for_query

    q = await db.execute(select(SRRequest).where(SRRequest.id == sr_id))
    sr = q.scalar_one_or_none()
    if not sr or sr.sr_type != SRType.INQUIRY:
        return {"success": False, "skip": True, "reason": "INQUIRY 타입 아님"}

    query = f"{sr.title} {sr.description or ''}"
    try:
        kb_result = await search_kb_for_query(query, limit=1)
    except Exception as e:
        return {"success": False, "error": str(e)[:100]}

    if not kb_result or kb_result[0].get("score", 0) < 0.75:
        return {"success": False, "skip": True, "reason": "KB 관련 문서 없음 (신뢰도 부족)"}

    top = kb_result[0]
    answer = (
        f"[자동 답변 — GUARDiA AI]\n\n"
        f"관련 KB 문서를 찾았습니다:\n\n"
        f"**{top.get('title', '')}**\n"
        f"{top.get('summary', '')[:500]}\n\n"
        f"도움이 되셨으면 이 SR을 완료 처리합니다.\n"
        f"추가 문의가 있으시면 새 SR을 등록해 주세요."
    )

    sr.status       = SRStatus.COMPLETED
    sr.description  = (sr.description or "") + f"\n\n---\n{answer}"
    await db.commit()

    return {
        "success": True,
        "sr_id":   sr.sr_id,
        "kb_title": top.get("title"),
        "auto_action": "kb_answer",
    }


# ── SLA 임박 자동 에스컬레이션 ──────────────────────────────────────────────

async def auto_escalate_sla(db: AsyncSession) -> list[dict]:
    """
    SLA 마감 30분 이내 미완료 SR → 자동 에스컬레이션.
    이미 에스컬레이션된 SR은 건너뜀.
    """
    from models import SRRequest, SRStatus

    threshold = datetime.now() + timedelta(minutes=30)
    q = await db.execute(
        select(SRRequest).where(
            and_(
                SRRequest.sla_deadline <= threshold,
                SRRequest.sla_deadline >= datetime.now(),
                SRRequest.status.not_in([SRStatus.COMPLETED, SRStatus.REJECTED]),
                SRRequest.escalated_at.is_(None),
                SRRequest.sla_breached == False,
            )
        ).limit(20)
    )
    srs = q.scalars().all()

    escalated = []
    for sr in srs:
        sr.escalated_at = datetime.now()
        sr.escalated_to = "ops-team"
        escalated.append({
            "sr_id": sr.sr_id,
            "title": sr.title,
            "deadline": sr.sla_deadline.isoformat() if sr.sla_deadline else None,
            "auto_action": "sla_escalate",
        })

    if escalated:
        await db.commit()

    return escalated


# ── 이상 감지 → 인시던트 자동 생성 ─────────────────────────────────────────

async def auto_create_incident_from_anomaly(db: AsyncSession,
                                             anomaly: dict) -> dict:
    """
    AI 이상 탐지 결과를 기반으로 인시던트 자동 생성.
    심각도 HIGH 이상만 자동 생성.
    """
    from models import SRRequest, SRStatus, SRType, Priority
    import uuid

    severity = anomaly.get("severity", "LOW")
    if severity not in ("HIGH", "CRITICAL"):
        return {"success": False, "skip": True, "reason": f"심각도 {severity} — 자동생성 기준 미달"}

    sr_id = f"INC-{datetime.now().strftime('%Y%m%d')}-{uuid.uuid4().hex[:4].upper()}"
    inc = SRRequest(
        sr_id=sr_id,
        sr_type=SRType.OTHER,
        title=f"[자동감지] {anomaly.get('description', '이상 감지')}",
        description=(
            f"AI 이상 탐지 자동 인시던트\n\n"
            f"서버: {anomaly.get('server', 'N/A')}\n"
            f"지표: {anomaly.get('metric', 'N/A')}\n"
            f"값: {anomaly.get('value', 'N/A')}\n"
            f"임계값: {anomaly.get('threshold', 'N/A')}\n"
            f"감지시각: {datetime.now().isoformat()}"
        ),
        priority=Priority.CRITICAL if severity == "CRITICAL" else Priority.HIGH,
        status=SRStatus.RECEIVED,
        requested_by="AUTO-SYSTEM",
    )
    db.add(inc)
    await db.commit()
    await db.refresh(inc)

    return {
        "success": True,
        "sr_id": sr_id,
        "incident_id": inc.id,
        "auto_action": "anomaly_notify",
    }


# ── 완료 SR/인시던트 → KB 아티클 초안 생성 ─────────────────────────────────

async def auto_draft_kb_article(db: AsyncSession, sr_id: int) -> dict:
    """
    완료된 SR/인시던트에서 KB 아티클 초안 자동 생성 (Ollama).
    초안 상태로 저장 — KB 담당자가 검토 후 게시.
    """
    from models import SRRequest, SRStatus
    from core.llm_client import call_llm

    q = await db.execute(
        select(SRRequest).where(
            SRRequest.id == sr_id,
            SRRequest.status == SRStatus.COMPLETED,
        )
    )
    sr = q.scalar_one_or_none()
    if not sr:
        return {"success": False, "skip": True}

    prompt = (
        f"다음 SR/인시던트 해결 내용을 바탕으로 KB 아티클 초안을 작성해줘:\n\n"
        f"제목: {sr.title}\n"
        f"내용: {(sr.description or '')[:500]}\n\n"
        f"형식: 문제 설명 / 원인 / 해결 방법 / 예방 조치 (각 섹션 2~3줄)"
    )
    try:
        draft = await call_llm(prompt, max_tokens=400)
    except Exception as e:
        return {"success": False, "error": str(e)[:100]}

    if not draft:
        return {"success": False, "error": "LLM 응답 없음"}

    # KnowledgeBase 모델에 초안 저장
    try:
        from models import KnowledgeBase
        kb = KnowledgeBase(
            title=f"[초안] {sr.title}",
            content=draft,
            category="자동생성",
            tags="auto,draft",
            is_draft=True,
            created_by="AUTO-SYSTEM",
            source_sr_id=sr.sr_id,
        )
        db.add(kb)
        await db.commit()
        return {
            "success": True,
            "sr_id":   sr.sr_id,
            "kb_title": kb.title,
            "auto_action": "kb_draft",
        }
    except Exception as e:
        # KB 모델이 없거나 필드 불일치 시 스킵
        logger.warning("KB draft skip: %s", e)
        return {"success": False, "skip": True, "reason": str(e)[:80]}


# ── 승인 요청 메시지 생성 ────────────────────────────────────────────────────

def build_approval_message(action: dict) -> str:
    """
    승인이 필요한 작업에 대한 메신저 봇 승인 요청 메시지 생성.
    """
    action_id   = action.get("action_id", "N/A")
    action_type = action.get("action_type", "N/A")
    description = action.get("description", "")
    risk        = action.get("risk", "HIGH")
    target      = action.get("target", "N/A")
    requested_by = action.get("requested_by", "SYSTEM")

    icon = {"HIGH": "⚠️", "CRITICAL": "🚨"}.get(risk, "❓")

    return (
        f"{icon} [승인 요청] {action_type}\n"
        f"━━━━━━━━━━━━━━━━━━━━\n"
        f"요청 ID: {action_id}\n"
        f"작업: {description}\n"
        f"대상: {target}\n"
        f"위험도: {risk}\n"
        f"요청자: {requested_by}\n"
        f"━━━━━━━━━━━━━━━━━━━━\n"
        f"✅ 승인: /approve {action_id}\n"
        f"❌ 거부: /reject {action_id} [사유]\n"
        f"⏰ 미응답 시 30분 후 자동 에스컬레이션"
    )


# ── 자율 처리 메인 루프 ──────────────────────────────────────────────────────

async def run_auto_processing_cycle(db: AsyncSession) -> dict:
    """
    5분마다 스케줄러에서 호출되는 자동 처리 사이클.
    Returns: 처리 결과 요약
    """
    results = {
        "auto_processed": [],
        "approval_requested": [],
        "skipped": [],
        "errors": [],
        "ran_at": datetime.now().isoformat(),
    }

    # 1. 신규 RECEIVED SR 자동 분류·배정
    from models import SRRequest, SRStatus, SRType
    q = await db.execute(
        select(SRRequest).where(
            SRRequest.status == SRStatus.RECEIVED,
            SRRequest.assigned_to.is_(None),
        ).limit(20)
    )
    new_srs = q.scalars().all()

    for sr in new_srs:
        try:
            r = await auto_classify_sr(db, sr.id)
            if r["success"]:
                results["auto_processed"].append(r)
                # INQUIRY 타입이면 KB 자동 답변 시도
                if sr.sr_type == SRType.INQUIRY:
                    r2 = await auto_answer_inquiry(db, sr.id)
                    if r2.get("success"):
                        results["auto_processed"].append(r2)
                    elif not r2.get("skip"):
                        results["errors"].append(r2)
        except Exception as e:
            results["errors"].append({"sr_id": getattr(sr, "sr_id", "?"), "error": str(e)[:80]})

    # 2. SLA 임박 SR 자동 에스컬레이션
    try:
        escalated = await auto_escalate_sla(db)
        results["auto_processed"].extend(escalated)
    except Exception as e:
        results["errors"].append({"action": "sla_escalate", "error": str(e)[:80]})

    # 3. 완료된 SR 중 KB 초안 미생성 항목 처리 (최근 1시간 이내 완료)
    try:
        cutoff = datetime.now() - timedelta(hours=1)
        q2 = await db.execute(
            select(SRRequest).where(
                SRRequest.status == SRStatus.COMPLETED,
                SRRequest.updated_at >= cutoff,
            ).limit(5)
        )
        recent_done = q2.scalars().all()
        for sr in recent_done:
            r3 = await auto_draft_kb_article(db, sr.id)
            if r3.get("success"):
                results["auto_processed"].append(r3)
    except Exception as e:
        results["errors"].append({"action": "kb_draft", "error": str(e)[:80]})

    # 결과 요약 로깅
    logger.info(
        "[AutoProcessor] 자동처리 %d건, 승인요청 %d건, 오류 %d건",
        len(results["auto_processed"]),
        len(results["approval_requested"]),
        len(results["errors"]),
    )
    return results
