"""
Slack 커넥터 — Incoming Webhook + Slash Commands + Block Kit

기능:
  - Slack으로 GUARDiA 이벤트 알림 발송
  - Slack Slash Commands 수신 (/guardia status, /guardia sr)
  - Block Kit 리치 메시지 (버튼·섹션·헤더)
  - 기존 메신저봇 명령어와 연동

엔드포인트:
  POST /api/slack/config          — Slack 설정 등록
  GET  /api/slack/config          — 설정 조회
  POST /api/slack/notify          — 알림 발송
  POST /api/slack/commands        — Slack Slash Commands 수신
  GET  /api/slack/test            — 테스트 메시지 발송
"""
from __future__ import annotations

import hashlib
import hmac
import json
import logging
import time
from datetime import datetime
from typing import Optional

import httpx
from fastapi import APIRouter, Depends, Form, Header, HTTPException, Request
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from core.auth import get_current_user, require_admin_role
from database import get_db
from models import User, SRRequest, SRStatus, SlackConfig  # 신규 모델

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/slack", tags=["Slack 연동"])


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

class SlackConfigCreate(BaseModel):
    name: str = Field(..., max_length=100)
    webhook_url: str = Field(..., description="Incoming Webhook URL")
    signing_secret: Optional[str] = None
    default_channel: str = Field("#guardia-ops", max_length=100)
    notify_sr_create: bool = True
    notify_incident: bool = True
    notify_deploy: bool = True
    notify_sla_breach: bool = True

class SlackNotifyRequest(BaseModel):
    channel: Optional[str] = None
    text: str
    level: str = Field("info", pattern="^(info|warning|error|success)$")
    sr_id: Optional[int] = None


# ── Block Kit 빌더 ───────────────────────────────────────────────────────────

def _color_for_level(level: str) -> str:
    return {"info": "#003366", "warning": "#F59E0B", "error": "#EF4444", "success": "#10B981"}.get(level, "#003366")


def _build_sr_blocks(sr: SRRequest) -> list:
    """SR 알림용 Block Kit."""
    return [
        {
            "type": "header",
            "text": {"type": "plain_text", "text": f"🎫 SR-{sr.id} {sr.title[:50]}"}
        },
        {
            "type": "section",
            "fields": [
                {"type": "mrkdwn", "text": f"*상태*\n{sr.status}"},
                {"type": "mrkdwn", "text": f"*우선순위*\n{sr.priority or 'MEDIUM'}"},
                {"type": "mrkdwn", "text": f"*카테고리*\n{sr.category or '-'}"},
                {"type": "mrkdwn", "text": f"*등록일*\n{sr.created_at.strftime('%m/%d %H:%M') if sr.created_at else '-'}"},
            ]
        },
        {
            "type": "actions",
            "elements": [
                {
                    "type": "button",
                    "text": {"type": "plain_text", "text": "상세 보기"},
                    "url": f"https://zioinfo.co.kr:8443/sr/{sr.id}",
                    "style": "primary",
                },
            ]
        }
    ]


def _build_text_blocks(text: str, level: str) -> list:
    """일반 텍스트 알림용 Block Kit."""
    emoji = {"info": "ℹ️", "warning": "⚠️", "error": "❌", "success": "✅"}.get(level, "ℹ️")
    return [
        {
            "type": "section",
            "text": {"type": "mrkdwn", "text": f"{emoji} {text}"}
        },
        {"type": "context", "elements": [
            {"type": "mrkdwn", "text": f"GUARDiA ITSM | {datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC')}"}
        ]}
    ]


# ── Slack API 발송 ────────────────────────────────────────────────────────────

async def _send_slack(webhook_url: str, payload: dict) -> bool:
    try:
        async with httpx.AsyncClient(timeout=10) as client:
            r = await client.post(webhook_url, json=payload)
            return r.status_code == 200
    except Exception as e:
        logger.error(f"Slack 발송 실패: {e}")
        return False


# ── 서명 검증 ────────────────────────────────────────────────────────────────

def _verify_slack_signature(signing_secret: str, body: bytes, timestamp: str, signature: str) -> bool:
    """Slack 요청 서명 검증 (HMAC-SHA256)."""
    if abs(time.time() - float(timestamp)) > 300:
        return False  # 5분 이상 오래된 요청 거부
    base = f"v0:{timestamp}:{body.decode()}"
    expected = "v0=" + hmac.new(
        signing_secret.encode(), base.encode(), hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)


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

@router.post("/config")
async def save_slack_config(
    req: SlackConfigCreate,
    db: AsyncSession = Depends(get_db),
    user: User = Depends(require_admin_role),
):
    """Slack 설정 저장."""
    existing = await db.execute(
        select(SlackConfig).where(SlackConfig.tenant_id == user.tenant_id)
    )
    cfg = existing.scalar_one_or_none()

    if cfg:
        cfg.webhook_url = req.webhook_url
        cfg.signing_secret = req.signing_secret
        cfg.default_channel = req.default_channel
        cfg.notify_sr_create = req.notify_sr_create
        cfg.notify_incident = req.notify_incident
        cfg.notify_deploy = req.notify_deploy
        cfg.notify_sla_breach = req.notify_sla_breach
    else:
        cfg = SlackConfig(
            tenant_id=user.tenant_id,
            name=req.name,
            webhook_url=req.webhook_url,
            signing_secret=req.signing_secret,
            default_channel=req.default_channel,
            notify_sr_create=req.notify_sr_create,
            notify_incident=req.notify_incident,
            notify_deploy=req.notify_deploy,
            notify_sla_breach=req.notify_sla_breach,
            is_active=True,
        )
        db.add(cfg)
    await db.commit()
    return {"ok": True}


@router.get("/config")
async def get_slack_config(
    db: AsyncSession = Depends(get_db),
    user: User = Depends(get_current_user),
):
    row = await db.execute(select(SlackConfig).where(SlackConfig.tenant_id == user.tenant_id))
    cfg = row.scalar_one_or_none()
    if not cfg:
        return None
    return {
        "name": cfg.name,
        "webhook_url": cfg.webhook_url[:20] + "***",  # 마스킹
        "default_channel": cfg.default_channel,
        "is_active": cfg.is_active,
    }


@router.get("/test")
async def test_slack(
    db: AsyncSession = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """테스트 메시지 발송."""
    row = await db.execute(select(SlackConfig).where(SlackConfig.tenant_id == user.tenant_id))
    cfg = row.scalar_one_or_none()
    if not cfg:
        raise HTTPException(404, "Slack 설정 없음")

    payload = {
        "channel": cfg.default_channel,
        "blocks": _build_text_blocks(f"GUARDiA ITSM 연동 테스트 메시지 (by {user.email})", "success"),
    }
    ok = await _send_slack(cfg.webhook_url, payload)
    return {"ok": ok}


@router.post("/notify")
async def slack_notify(
    req: SlackNotifyRequest,
    db: AsyncSession = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """Slack 알림 발송."""
    row = await db.execute(select(SlackConfig).where(SlackConfig.tenant_id == user.tenant_id))
    cfg = row.scalar_one_or_none()
    if not cfg:
        raise HTTPException(404, "Slack 설정 없음")

    # SR 첨부 시 Block Kit 사용
    if req.sr_id:
        sr_row = await db.execute(select(SRRequest).where(SRRequest.id == req.sr_id))
        sr = sr_row.scalar_one_or_none()
        if sr:
            payload = {
                "channel": req.channel or cfg.default_channel,
                "text": req.text,
                "attachments": [{"color": _color_for_level(req.level), "blocks": _build_sr_blocks(sr)}],
            }
        else:
            payload = {"channel": req.channel or cfg.default_channel,
                       "blocks": _build_text_blocks(req.text, req.level)}
    else:
        payload = {"channel": req.channel or cfg.default_channel,
                   "blocks": _build_text_blocks(req.text, req.level)}

    ok = await _send_slack(cfg.webhook_url, payload)
    return {"ok": ok}


@router.post("/commands")
async def slack_commands(
    request: Request,
    x_slack_signature: str = Header(None, alias="X-Slack-Signature"),
    x_slack_request_timestamp: str = Header(None, alias="X-Slack-Request-Timestamp"),
    db: AsyncSession = Depends(get_db),
):
    """
    Slack Slash Commands 처리.
    Slack 앱에서 /guardia 명령어 설정 필요:
    Request URL: https://zioinfo.co.kr:8443/api/slack/commands
    """
    body = await request.body()

    # 서명 검증 (설정된 경우)
    # 모든 테넌트 설정 조회는 생략, 실제로는 team_id 기반 조회
    form_data = {}
    for item in body.decode().split("&"):
        if "=" in item:
            k, v = item.split("=", 1)
            form_data[k] = v.replace("+", " ")

    command = form_data.get("command", "")
    text = form_data.get("text", "").strip()
    user_name = form_data.get("user_name", "unknown")

    if command == "/guardia":
        if text.startswith("status"):
            # 시스템 현황 반환
            return {
                "response_type": "in_channel",
                "blocks": [
                    {"type": "header", "text": {"type": "plain_text", "text": "🛡️ GUARDiA 현황"}},
                    {"type": "section", "text": {"type": "mrkdwn",
                        "text": f"@{user_name}님이 조회했습니다. <https://zioinfo.co.kr:8443|대시보드 보기>"}},
                ]
            }
        elif text.startswith("sr"):
            return {
                "response_type": "ephemeral",
                "text": "SR 접수는 웹 대시보드를 이용하거나 /sr 명령어를 사용하세요.",
            }
        else:
            return {
                "response_type": "ephemeral",
                "text": "사용법: `/guardia status` | `/guardia sr`",
            }

    return {"response_type": "ephemeral", "text": "알 수 없는 명령어입니다."}
