"""
GUARDiA ITSM 알림 모듈 — 이메일(SMTP) + 메신저(Webhook) 자동 발송.

설정은 환경 변수로 관리 (외부 API 호출 금지, 온프레미스 전용):

  이메일 설정:
    SMTP_HOST          = ""              # 빈 문자열이면 이메일 비활성화
    SMTP_PORT          = "25"
    SMTP_USER          = ""
    SMTP_PASSWORD      = ""
    SMTP_FROM          = "noreply@guardia.local"
    SMTP_TLS           = "false"         # SSL/TLS (포트 465 등)
    SMTP_STARTTLS      = "false"         # STARTTLS (포트 587 등)

  메신저 설정:
    MESSENGER_WEBHOOK  = "http://localhost:8000/api/messenger/webhook"
    MESSENGER_ENABLED  = "true"
    MESSENGER_OPS_ROOM = "ops"           # 운영팀 알림 채널

  알림 트리거:
    NOTIFY_ON_CREATED  = "true"          # SR 접수 시 고객 이메일 발송
    NOTIFY_STATUSES    = "COMPLETED,REJECTED,FAILED_ROLLBACK"  # 상태 변경 시 발송
"""
import asyncio
import os
import smtplib
import socket
from datetime import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from typing import Optional

try:
    import httpx
    _HTTPX = True
except ImportError:
    _HTTPX = False

# ── 환경변수 설정 ──────────────────────────────────────────────────────────────

def _env(key: str, default: str = "") -> str:
    return os.environ.get(key, default).strip()

def _env_bool(key: str, default: bool = True) -> bool:
    v = os.environ.get(key, "").strip().lower()
    if v in ("1", "true", "yes", "on"):
        return True
    if v in ("0", "false", "no", "off"):
        return False
    return default


# ── 이메일 HTML 템플릿 ─────────────────────────────────────────────────────────

_BRAND_COLOR = "#1E3A5F"

def _html_email(title: str, body_html: str, sr_id: str = "", status_badge: str = "") -> str:
    badge_html = f'<span style="background:{_status_color(status_badge)};color:#fff;padding:3px 10px;border-radius:12px;font-size:13px;font-weight:bold">{status_badge}</span>' if status_badge else ""
    return f"""<!DOCTYPE html>
<html lang="ko">
<head><meta charset="UTF-8">
<style>
  body{{font-family:'맑은 고딕',Arial,sans-serif;background:#f4f6f9;margin:0;padding:0}}
  .wrap{{max-width:600px;margin:30px auto;background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.1)}}
  .hdr{{background:{_BRAND_COLOR};padding:24px 32px;color:#fff}}
  .hdr h1{{margin:0;font-size:20px;font-weight:700}}
  .hdr .sub{{margin:4px 0 0;font-size:13px;opacity:.8}}
  .body{{padding:28px 32px;color:#333;line-height:1.6}}
  .sr-id{{font-family:monospace;background:#f0f4f8;padding:4px 10px;border-radius:4px;font-size:14px;color:{_BRAND_COLOR}}}
  .info-table{{width:100%;border-collapse:collapse;margin:16px 0}}
  .info-table th{{text-align:left;width:30%;padding:8px 12px;background:#f7f9fc;color:#555;font-size:13px;border:1px solid #e0e6ed}}
  .info-table td{{padding:8px 12px;font-size:14px;border:1px solid #e0e6ed}}
  .result-box{{background:#f7f9fc;border-left:4px solid {_BRAND_COLOR};padding:12px 16px;margin:16px 0;border-radius:0 4px 4px 0;font-size:14px;white-space:pre-wrap}}
  .ftr{{background:#f4f6f9;padding:16px 32px;text-align:center;font-size:12px;color:#888}}
</style>
</head>
<body>
<div class="wrap">
  <div class="hdr">
    <h1>GUARDiA ITSM</h1>
    <div class="sub">인프라 서비스 요청 관리 시스템</div>
  </div>
  <div class="body">
    <p style="font-size:17px;font-weight:bold;margin-top:0">{title} {badge_html}</p>
    {body_html}
  </div>
  <div class="ftr">본 메일은 GUARDiA ITSM에서 자동 발송되었습니다. 문의: guardia@your-org.local</div>
</div>
</body>
</html>"""


def _status_color(status: str) -> str:
    return {
        "COMPLETED":        "#1a7f4b",
        "REJECTED":         "#c0392b",
        "FAILED_ROLLBACK":  "#e67e22",
        "IN_PROGRESS":      "#2980b9",
        "APPROVED":         "#27ae60",
        "PENDING_APPROVAL": "#8e44ad",
        "RECEIVED":         "#2c3e50",
    }.get(status, "#555")


def _status_ko(status: str) -> str:
    return {
        "RECEIVED":              "접수",
        "PARSED":                "분석 완료",
        "PENDING_APPROVAL":      "승인 대기",
        "APPROVED":              "승인됨",
        "IN_PROGRESS":           "처리 중",
        "PENDING_PM_VALIDATION": "PM 검토 중",
        "COMPLETED":             "처리 완료",
        "FAILED_ROLLBACK":       "처리 실패 (롤백)",
        "REJECTED":              "반려",
    }.get(status, status)


# ── SMTP 발송 ─────────────────────────────────────────────────────────────────

def _smtp_send_sync(to_addrs: list[str], subject: str, html_body: str) -> None:
    """동기 SMTP 발송 — asyncio.to_thread()로 호출."""
    host = _env("SMTP_HOST")
    if not host:
        raise RuntimeError("SMTP_HOST not configured")

    port      = int(_env("SMTP_PORT", "25"))
    user      = _env("SMTP_USER")
    password  = _env("SMTP_PASSWORD")
    from_addr = _env("SMTP_FROM", "noreply@guardia.local")
    use_tls   = _env_bool("SMTP_TLS", False)
    use_start = _env_bool("SMTP_STARTTLS", False)

    msg = MIMEMultipart("alternative")
    msg["Subject"] = subject
    msg["From"]    = from_addr
    msg["To"]      = ", ".join(to_addrs)
    msg.attach(MIMEText(html_body, "html", "utf-8"))

    timeout = 10  # seconds
    if use_tls:
        with smtplib.SMTP_SSL(host, port, timeout=timeout) as s:
            if user:
                s.login(user, password)
            s.sendmail(from_addr, to_addrs, msg.as_string())
    else:
        with smtplib.SMTP(host, port, timeout=timeout) as s:
            if use_start:
                s.ehlo()
                s.starttls()
                s.ehlo()
            if user:
                s.login(user, password)
            s.sendmail(from_addr, to_addrs, msg.as_string())


async def send_email(to_addrs: list[str], subject: str, html_body: str) -> tuple[bool, str]:
    """이메일 비동기 발송. (성공여부, 오류메시지) 반환."""
    if not to_addrs:
        return False, "수신자 없음"
    host = _env("SMTP_HOST")
    if not host:
        return False, "SMTP_HOST 미설정"
    try:
        await asyncio.to_thread(_smtp_send_sync, to_addrs, subject, html_body)
        return True, ""
    except (smtplib.SMTPException, socket.error, OSError, RuntimeError) as exc:
        return False, str(exc)[:200]


# ── 메신저 Webhook 발송 ───────────────────────────────────────────────────────

async def send_messenger(room: str, payload: dict) -> tuple[bool, str]:
    """메신저 webhook 비동기 발송. (성공여부, 오류메시지) 반환."""
    if not _HTTPX:
        return False, "httpx 미설치"
    webhook = _env("MESSENGER_WEBHOOK", "http://localhost:8000/api/messenger/webhook")
    if not webhook:
        return False, "MESSENGER_WEBHOOK 미설정"
    try:
        async with httpx.AsyncClient(timeout=5.0) as client:
            await client.post(webhook, json={"room": room, **payload})
        return True, ""
    except Exception as exc:
        return False, str(exc)[:200]


# ── 알림 이력 저장 ─────────────────────────────────────────────────────────────

async def _log(sr_id: Optional[str], channel: str, recipient: str,
               subject: str, ok: bool, error: str) -> None:
    """발송 결과를 tb_notification_log에 기록 (독립 세션 사용)."""
    try:
        from database import SessionLocal
        from models import NotificationLog
        async with SessionLocal() as db:
            db.add(NotificationLog(
                sr_id=sr_id, channel=channel, recipient=recipient,
                subject=subject,
                status="SENT" if ok else "FAILED",
                error_msg=error if not ok else None,
            ))
            await db.commit()
    except Exception:
        pass  # 알림 로그 실패는 묵인


# ── 수신자 조회 ────────────────────────────────────────────────────────────────

async def _get_recipient_emails(requested_by: str, inst_id: Optional[int]) -> list[str]:
    """SR의 요청자 이메일 + 기관 주 담당자 이메일 수집 (독립 세션 사용)."""
    from database import SessionLocal
    from sqlalchemy import select
    from models import User, InstContact

    addrs: list[str] = []

    async with SessionLocal() as db:
        # 1. 요청자 User 이메일
        try:
            r = await db.execute(select(User).where(User.username == requested_by))
            user = r.scalars().first()
            if user and user.email:
                addrs.append(user.email.strip())
        except Exception:
            pass

        # 2. 기관 주 담당자 이메일
        if inst_id:
            try:
                r = await db.execute(
                    select(InstContact).where(
                        InstContact.inst_id == inst_id,
                        InstContact.is_primary == True,
                        InstContact.is_active == True,
                    ).limit(1)
                )
                contact = r.scalars().first()
                if contact and contact.email:
                    e = contact.email.strip()
                    if e not in addrs:
                        addrs.append(e)
            except Exception:
                pass

    return addrs


# ── 공개 알림 함수 ─────────────────────────────────────────────────────────────
# sr 객체는 ORM 세션이 닫힌 뒤에도 동작하도록 필요한 필드 값만 사용

async def notify_sr_created(sr, db=None) -> None:
    """SR 접수 시 고객에게 접수 확인 이메일 + 메신저 알림 발송."""
    if not _env_bool("NOTIFY_ON_CREATED", True):
        return

    # ORM 세션 닫혀도 안전하게 필드 캡처
    sr_id   = sr.sr_id
    title   = sr.title
    sr_type = sr.sr_type
    prio    = sr.priority
    req_by  = sr.requested_by
    inst_id = sr.inst_id

    # ── 이메일 ─────────────────────────────────────────────────────────────
    addrs = await _get_recipient_emails(req_by, inst_id)
    if addrs:
        subject   = f"[GUARDiA] {sr_id} 접수 확인 — {title}"
        body_html = f"""
<p>안녕하세요.</p>
<p>서비스 요청이 정상적으로 접수되었습니다. 담당 엔지니어가 배정되면 작업을 시작합니다.</p>
<table class="info-table">
  <tr><th>SR 번호</th>  <td><span class="sr-id">{sr_id}</span></td></tr>
  <tr><th>제목</th>     <td>{title}</td></tr>
  <tr><th>유형</th>     <td>{sr_type}</td></tr>
  <tr><th>우선순위</th> <td>{prio}</td></tr>
  <tr><th>요청자</th>   <td>{req_by}</td></tr>
  <tr><th>접수 시각</th><td>{datetime.now().strftime('%Y-%m-%d %H:%M')}</td></tr>
</table>
<p style="color:#666;font-size:13px">진행 상황은 GUARDiA ITSM 포털에서 실시간으로 확인하실 수 있습니다.</p>"""
        html = _html_email("SR 접수 확인", body_html, sr_id, "RECEIVED")
        ok, err = await send_email(addrs, subject, html)
        asyncio.create_task(_log(sr_id, "EMAIL", ", ".join(addrs), subject, ok, err))

    # ── 메신저 ─────────────────────────────────────────────────────────────
    if _env_bool("MESSENGER_ENABLED", True):
        room = _env("MESSENGER_OPS_ROOM", "ops")
        ok, err = await send_messenger(room, {
            "event":        "sr_created",
            "sr_id":        sr_id,
            "title":        title,
            "sr_type":      sr_type,
            "priority":     prio,
            "requested_by": req_by,
            "message":      f"[접수] {sr_id} — {title} ({sr_type} / {prio})",
        })
        asyncio.create_task(_log(sr_id, "MESSENGER", room, f"SR 접수: {sr_id}", ok, err))


async def notify_sr_status_changed(sr, new_status: str, summary: str, db=None) -> None:
    """SR 상태 변경 시 이메일 + 메신저 알림 발송."""
    notify_statuses = _env("NOTIFY_STATUSES", "COMPLETED,REJECTED,FAILED_ROLLBACK").split(",")
    if new_status not in notify_statuses:
        return

    # 필드 캡처
    sr_id   = sr.sr_id
    title   = sr.title
    sr_type = sr.sr_type
    inst_id = sr.inst_id
    req_by  = sr.requested_by
    status_ko = _status_ko(new_status)
    color     = _status_color(new_status)

    # ── 이메일 ─────────────────────────────────────────────────────────────
    addrs = await _get_recipient_emails(req_by, inst_id)
    if addrs:
        subject = f"[GUARDiA] {sr_id} {status_ko} — {title}"

        if new_status == "COMPLETED":
            intro = "서비스 요청이 정상적으로 처리 완료되었습니다."
        elif new_status == "REJECTED":
            intro = "서비스 요청이 반려되었습니다. 내용을 확인 후 재요청 바랍니다."
        else:
            intro = "서비스 요청 처리 중 오류가 발생하여 롤백되었습니다. 담당자에게 문의해 주세요."

        body_html = f"""
<p>안녕하세요.</p>
<p>{intro}</p>
<table class="info-table">
  <tr><th>SR 번호</th>  <td><span class="sr-id">{sr_id}</span></td></tr>
  <tr><th>제목</th>     <td>{title}</td></tr>
  <tr><th>처리 상태</th><td><strong style="color:{color}">{status_ko}</strong></td></tr>
  <tr><th>처리 시각</th><td>{datetime.now().strftime('%Y-%m-%d %H:%M')}</td></tr>
</table>
<p><strong>처리 결과 요약</strong></p>
<div class="result-box">{summary or "상세 결과 없음"}</div>
<p style="color:#666;font-size:13px">세부 처리 내역은 GUARDiA ITSM 포털에서 확인하실 수 있습니다.</p>"""
        html = _html_email(f"SR {status_ko}", body_html, sr_id, new_status)
        ok, err = await send_email(addrs, subject, html)
        asyncio.create_task(_log(sr_id, "EMAIL", ", ".join(addrs), subject, ok, err))

    # ── 메신저 ─────────────────────────────────────────────────────────────
    if _env_bool("MESSENGER_ENABLED", True):
        room = _env("MESSENGER_OPS_ROOM", "ops")
        emoji = {"COMPLETED": "✅", "REJECTED": "❌", "FAILED_ROLLBACK": "⚠️"}.get(new_status, "ℹ️")
        ok, err = await send_messenger(room, {
            "event":          "sr_status_changed",
            "sr_id":          sr_id,
            "title":          title,
            "sr_type":        sr_type,
            "new_status":     new_status,
            "result_summary": summary[:200] if summary else "",
            "message":        f"{emoji} [{status_ko}] {sr_id} — {title}\n{summary[:100] if summary else ''}",
        })
        asyncio.create_task(_log(sr_id, "MESSENGER", room,
                                 f"SR {status_ko}: {sr_id}", ok, err))


# ═══════════════════════════════════════════════════════════════════════════════
# ── A-3: 배포 승인 모바일 알림 ──────────────────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════════

async def notify_deploy_approval_required(
    session_id: int,
    sr_id: Optional[str],
    project_name: str,
    approvers: list[str],
    approve_url: str = "",
) -> None:
    """
    배포 승인 대기 알림.

    vibe 세션이 PENDING_APPROVAL 상태에 진입할 때 호출.
    지정된 승인자(approvers)들에게 메신저 + 이메일 알림 발송.

    Args:
        session_id:   VibeSession.id
        sr_id:        연관된 SR ID (없을 수 있음)
        project_name: 배포 대상 프로젝트명
        approvers:    승인 담당자 username 목록 (User.email로 변환)
        approve_url:  승인 링크 (딥링크 또는 웹 URL)
    """
    from database import SessionLocal
    from sqlalchemy import select
    from models import User

    subject = f"[GUARDiA] 배포 승인 요청 — {project_name} (세션 #{session_id})"
    ops_room = _env("MESSENGER_OPS_ROOM", "ops")
    approve_room = _env("MESSENGER_APPROVE_ROOM", ops_room)

    # 메신저 알림
    msg = (
        f"🚀 배포 승인 요청\n"
        f"프로젝트: {project_name}\n"
        f"세션 ID: #{session_id}\n"
        f"SR: {sr_id or '(없음)'}\n"
        f"승인 필요 담당자: {', '.join(approvers)}\n"
        f"승인 링크: {approve_url or '(관리 UI 확인)'}"
    )
    ok, err = await send_messenger(approve_room, {
        "event":        "deploy_approval_required",
        "session_id":   session_id,
        "project":      project_name,
        "approvers":    approvers,
        "approve_url":  approve_url,
        "type":         "text",
        "text":         msg,
    })

    # 이메일 알림 (approvers → User.email 조회)
    if approvers:
        try:
            async with SessionLocal() as db:
                result = await db.execute(
                    select(User).where(User.username.in_(approvers))
                )
                users = result.scalars().all()
                email_addrs = [u.email for u in users if u.email]

            if email_addrs:
                body_html = f"""
<p>안녕하세요.</p>
<p>배포 승인이 필요합니다.</p>
<table class="info-table">
  <tr><th>프로젝트</th>  <td>{project_name}</td></tr>
  <tr><th>세션 ID</th>   <td>#{session_id}</td></tr>
  <tr><th>SR 번호</th>   <td>{sr_id or '(없음)'}</td></tr>
  <tr><th>요청 시각</th> <td>{datetime.now().strftime('%Y-%m-%d %H:%M')}</td></tr>
</table>
<p style="margin-top:16px">
  <a href="{approve_url}" style="background:#2563eb;color:#fff;padding:10px 20px;border-radius:4px;text-decoration:none;font-weight:bold">
    배포 승인하기
  </a>
</p>
<p style="color:#888;font-size:13px">위 버튼이 작동하지 않으면 GUARDiA ITSM 관리 UI에서 직접 승인해 주세요.</p>
"""
                html = _html_email(f"배포 승인 요청: {project_name}", body_html, str(session_id))
                email_ok, email_err = await send_email(email_addrs, subject, html)
                asyncio.create_task(_log(sr_id, "EMAIL", ",".join(email_addrs), subject, email_ok, email_err))
        except Exception:
            pass


async def notify_deploy_completed(
    session_id: int,
    sr_id: Optional[str],
    project_name: str,
    success: bool,
    summary: str = "",
) -> None:
    """
    배포 완료/실패 알림.

    vibe 세션이 COMPLETED 또는 FAILED 상태에 진입할 때 호출.
    """
    ops_room = _env("MESSENGER_OPS_ROOM", "ops")
    emoji = "✅" if success else "❌"
    status_text = "완료" if success else "실패"

    msg = (
        f"{emoji} 배포 {status_text}\n"
        f"프로젝트: {project_name}\n"
        f"세션 ID: #{session_id}\n"
        f"SR: {sr_id or '(없음)'}\n"
        f"일시: {datetime.now().strftime('%Y-%m-%d %H:%M')}"
        + (f"\n결과: {summary[:200]}" if summary else "")
    )
    ok, err = await send_messenger(ops_room, {
        "event":      "deploy_completed",
        "session_id": session_id,
        "project":    project_name,
        "success":    success,
        "type":       "text",
        "text":       msg,
    })
    asyncio.create_task(_log(sr_id, "MESSENGER", ops_room,
                             f"배포 {status_text}: {project_name}", ok, err))
