#!/usr/bin/env python3
"""
GUARDiA CMS — 중앙 guardia-rag 색인 훅 (브랜드 톤·용어집·콘텐츠 가이드 → cms 컬렉션).

콘텐츠 초안 생성(/answer hybrid·/agent)의 근거 회수와 용어 일관성을 위해, CMS 의 **브랜드 톤·
용어집·콘텐츠 유형 가이드·SEO/모더레이션 정책** 메타만 중앙 RAG 의 `cms` 컬렉션에 적재한다.
회원 PII·자격증명·실데이터 행은 색인하지 않는다(보안 불변).

선택: 라이브 published 콘텐츠 본문도 색인하려면 --with-content 로 공개 delivery API(GET, 인증불요)
에서 published 만 가져와 색인한다(초안/비공개 제외, PII 필드 제외).

사용:
    python scripts/rag_ingest_cms.py                         # 기본 메타 docs (http://127.0.0.1:8020)
    RAG_URL=http://127.0.0.1:8020 python scripts/rag_ingest_cms.py
    CMS_URL=http://127.0.0.1:8012 python scripts/rag_ingest_cms.py --with-content

설계 메모:
- 외부 API 0. 중앙 guardia-rag(온프레미스)만 호출.
- 멱등: doc_id 안정 → 재실행 시 upsert(중앙이 처리). 실패해도 CMS 동작에 영향 없음(보조 스크립트).
- PII 미색인: 회원 email/phone/주소·password_enc·작성자 식별정보는 문서에 포함하지 않는다.
"""
import json
import os
import sys
import urllib.request
import urllib.error

RAG_URL = os.environ.get("RAG_URL", "http://127.0.0.1:8020").rstrip("/")
CMS_URL = os.environ.get("CMS_URL", "http://127.0.0.1:8012").rstrip("/")
SOLUTION = "cms"

# 브랜드 톤·용어집·콘텐츠 정책 메타 — 콘텐츠 초안 grounding·용어 일관성용.
DOCS = [
    {
        "doc_id": "cms_brand_tone",
        "source": "CMS-BRAND",
        "text": (
            "GUARDiA CMS 브랜드 톤 가이드: 친근하지만 신뢰감 있는 한국어. 과장·허위광고 금지, "
            "구체적 가치(품질·배송·혜택)를 명료하게. 이모지는 절제. 마케팅 카피는 200자 내외 권장."
        ),
    },
    {
        "doc_id": "cms_content_types",
        "source": "CMS-GUIDE",
        "text": (
            "콘텐츠 유형(content_type): PAGE(정적 페이지), POST(블로그/공지), BLOCK(재사용 블록), "
            "PRODUCT(상품 상세). 게시 워크플로우: draft→review→approved→published. "
            "AI 초안은 항상 draft 로만 적재하고 published 직행 금지(사람 승인)."
        ),
    },
    {
        "doc_id": "cms_glossary",
        "source": "CMS-GLOSSARY",
        "text": (
            "도메인 용어집(번역·작성 시 보존): 헤드리스(headless)=콘텐츠와 표현 분리, "
            "delivery API=published 콘텐츠 제공 API, UGC=사용자생성콘텐츠(리뷰/댓글/문의), "
            "기획전=프로모션 캠페인. 직역 금지, 용어 일관 유지."
        ),
    },
    {
        "doc_id": "cms_seo_policy",
        "source": "CMS-SEO",
        "text": (
            "SEO 가이드: meta title 60자·description 150자 내외, 키워드 3~8개, "
            "본문 길이 300~1500자 가독성 우수. 중복 콘텐츠·키워드 스터핑 금지."
        ),
    },
    {
        "doc_id": "cms_moderation_policy",
        "source": "CMS-MODERATION",
        "text": (
            "UGC 모더레이션 정책: 욕설/스팸(외부링크·도박·대출·주식리딩)/금칙어/개인정보 노출은 차단 또는 보류. "
            "판정 verdict: APPROVE/REJECT/REVIEW. 확신이 낮으면 자동 통과 금지 → REVIEW(사람 검토). "
            "개인정보(전화/이메일/주소/카드)는 pii_flag 로 표시하고 응답·색인에서 제외."
        ),
    },
]


def fetch_published_content():
    """공개 delivery API에서 published 콘텐츠 본문 일부를 docs 로 변환(옵션). PII/비공개 제외."""
    out = []
    try:
        req = urllib.request.Request(f"{CMS_URL}/api/cms/delivery/content?status=PUBLISHED",
                                     headers={"Accept": "application/json"}, method="GET")
        with urllib.request.urlopen(req, timeout=20) as resp:
            payload = json.loads(resp.read().decode("utf-8"))
        items = payload.get("data") if isinstance(payload, dict) else payload
        if not isinstance(items, list):
            return out
        for it in items[:200]:
            cid = it.get("id")
            title = (it.get("title") or "").strip()
            body = (it.get("body") or it.get("content") or "").strip()
            ctype = it.get("contentType") or it.get("content_type") or "CONTENT"
            if not title and not body:
                continue
            out.append({
                "doc_id": f"cms_content_{cid}",
                "source": f"CMS-{ctype}",
                "text": f"[{ctype}] {title}\n{body[:2000]}",
            })
    except Exception:
        # 보조 기능 — 라이브 미가용/형식 상이 시 조용히 스킵
        pass
    return out


def ingest(docs):
    payload = {"solution": SOLUTION, "documents": docs}
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        f"{RAG_URL}/rag/ingest",
        data=data,
        headers={"Content-Type": "application/json", "X-Solution-Key": SOLUTION},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=120) as resp:
            body = resp.read().decode("utf-8")
            print(f"[OK] ingested {len(docs)} docs into '{SOLUTION}' collection")
            print(f"     response: {body[:300]}")
            return 0
    except urllib.error.HTTPError as e:
        print(f"[WARN] ingest HTTP {e.code} — 중앙 RAG 응답 이상(스크립트는 보조). 본 CMS 동작 영향 없음.")
        return 1
    except urllib.error.URLError as e:
        print(f"[WARN] 중앙 guardia-rag 미가용({RAG_URL}) — 색인 보류. 라이브 가동 후 재실행. "
              f"(사유 요약: {str(e.reason)[:80]})")
        return 1
    except Exception:
        print("[WARN] 색인 중 예기치 못한 오류 — 보류(CMS 동작 영향 없음).")
        return 1


if __name__ == "__main__":
    docs = list(DOCS)
    if "--with-content" in sys.argv:
        extra = fetch_published_content()
        print(f"[INFO] published 콘텐츠 {len(extra)}건 추가 색인 대상")
        docs += extra
    sys.exit(ingest(docs))
