"""
ServiceNow 연동 커넥터

기능:
  - ServiceNow CMDB CI 목록 조회
  - Incident 양방향 동기화
  - GUARDiA SR → ServiceNow Incident 생성
  - ServiceNow Change Request 조회

엔드포인트:
  POST /api/servicenow/config        — 연동 설정
  GET  /api/servicenow/config        — 설정 조회
  POST /api/servicenow/test          — 연결 테스트
  GET  /api/servicenow/incidents     — Incident 목록
  POST /api/servicenow/sync/{sr_id}  — SR → ServiceNow Incident 생성
  GET  /api/servicenow/cmdb          — CMDB CI 목록
  GET  /api/servicenow/changes       — Change Request 목록
"""
from __future__ import annotations

import json
import logging
from datetime import datetime
from typing import Optional

import httpx
from fastapi import APIRouter, Depends, HTTPException
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, ServiceNowConfig, ServiceNowMapping  # 신규

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/servicenow", tags=["ServiceNow"])

PRIORITY_MAP = {"HIGH": "1", "MEDIUM": "2", "LOW": "3"}


class SNowConfigCreate(BaseModel):
    instance_url: str = Field(..., description="https://company.service-now.com")
    username: str
    password: str
    assignment_group: Optional[str] = None


async def _snow_request(cfg: ServiceNowConfig, method: str, path: str,
                         payload: Optional[dict] = None) -> Optional[dict]:
    url = f"{cfg.instance_url.rstrip('/')}/api/now/{path}"
    try:
        async with httpx.AsyncClient(timeout=15, verify=False) as client:
            r = await getattr(client, method.lower())(
                url, json=payload,
                auth=(cfg.username, cfg.password_enc),
                headers={"Content-Type": "application/json", "Accept": "application/json"}
            )
            return r.json() if r.status_code in (200, 201) else None
    except Exception as e:
        logger.error(f"ServiceNow API 실패: {e}")
        return None


@router.post("/config")
async def save_config(
    req: SNowConfigCreate,
    db: AsyncSession = Depends(get_db),
    user: User = Depends(require_admin_role),
):
    row = await db.execute(select(ServiceNowConfig).where(ServiceNowConfig.tenant_id == user.tenant_id))
    cfg = row.scalar_one_or_none()
    if cfg:
        cfg.instance_url = req.instance_url; cfg.username = req.username
        cfg.password_enc = req.password; cfg.assignment_group = req.assignment_group
    else:
        cfg = ServiceNowConfig(
            tenant_id=user.tenant_id, instance_url=req.instance_url,
            username=req.username, password_enc=req.password,
            assignment_group=req.assignment_group, is_active=True, created_at=datetime.utcnow()
        )
        db.add(cfg)
    await db.commit()
    return {"ok": True}


@router.post("/test")
async def test_connection(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
    row = await db.execute(select(ServiceNowConfig).where(ServiceNowConfig.tenant_id == user.tenant_id))
    cfg = row.scalar_one_or_none()
    if not cfg: raise HTTPException(404, "설정 없음")
    data = await _snow_request(cfg, "GET", "table/sys_user?sysparm_limit=1")
    return {"ok": bool(data), "instance": cfg.instance_url}


@router.get("/incidents")
async def list_incidents(limit: int = 20, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
    row = await db.execute(select(ServiceNowConfig).where(ServiceNowConfig.tenant_id == user.tenant_id))
    cfg = row.scalar_one_or_none()
    if not cfg: raise HTTPException(404, "설정 없음")
    data = await _snow_request(cfg, "GET", f"table/incident?sysparm_limit={limit}&sysparm_fields=number,short_description,state,priority,opened_at")
    records = (data or {}).get("result", [])
    return [{"number": r.get("number"), "title": r.get("short_description"),
             "state": r.get("state"), "priority": r.get("priority"), "opened_at": r.get("opened_at")} for r in records]


@router.post("/sync/{sr_id}")
async def sync_to_servicenow(sr_id: int, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
    """SR → ServiceNow Incident 생성."""
    sr_row = await db.execute(select(SRRequest).where(SRRequest.id == sr_id))
    sr = sr_row.scalar_one_or_none()
    if not sr: raise HTTPException(404, "SR 없음")
    cfg_row = await db.execute(select(ServiceNowConfig).where(ServiceNowConfig.tenant_id == user.tenant_id))
    cfg = cfg_row.scalar_one_or_none()
    if not cfg: raise HTTPException(404, "ServiceNow 설정 없음")

    payload = {
        "short_description": f"[GUARDiA SR-{sr.id}] {sr.title}",
        "description": sr.description or "",
        "impact": PRIORITY_MAP.get((sr.priority or "MEDIUM").upper(), "2"),
        "urgency": PRIORITY_MAP.get((sr.priority or "MEDIUM").upper(), "2"),
    }
    if cfg.assignment_group:
        payload["assignment_group"] = {"name": cfg.assignment_group}

    data = await _snow_request(cfg, "POST", "table/incident", payload)
    if data and data.get("result"):
        sn_number = data["result"].get("number", "")
        mapping = ServiceNowMapping(sr_id=sr.id, snow_number=sn_number, config_id=cfg.id, synced_at=datetime.utcnow())
        db.add(mapping)
        await db.commit()
        return {"ok": True, "snow_number": sn_number}
    raise HTTPException(500, "ServiceNow Incident 생성 실패")


@router.get("/cmdb")
async def list_cmdb(limit: int = 50, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
    row = await db.execute(select(ServiceNowConfig).where(ServiceNowConfig.tenant_id == user.tenant_id))
    cfg = row.scalar_one_or_none()
    if not cfg: raise HTTPException(404, "설정 없음")
    data = await _snow_request(cfg, "GET", f"table/cmdb_ci_server?sysparm_limit={limit}&sysparm_fields=name,ip_address,os,status")
    return (data or {}).get("result", [])


@router.get("/changes")
async def list_changes(limit: int = 20, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
    row = await db.execute(select(ServiceNowConfig).where(ServiceNowConfig.tenant_id == user.tenant_id))
    cfg = row.scalar_one_or_none()
    if not cfg: raise HTTPException(404, "설정 없음")
    data = await _snow_request(cfg, "GET", f"table/change_request?sysparm_limit={limit}&sysparm_fields=number,short_description,state,type,scheduled_start_date")
    return (data or {}).get("result", [])
