# -*- coding: utf-8 -*-
"""고객 시연용 AI 파트 발표 PPT 생성 — 시연 시나리오 순 구성.

근거: docs/demo/AI_DEMO_SCRIPT.pptx(내부 절차서)의 4개 데모 흐름.
      고객용이므로 계정·체크리스트·트러블슈팅 등 내부 정보는 제외.
출력: docs/demo/AI_시연_시나리오.pptx
"""
import os

BASE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "docs", "demo")
KOR_FONT = "맑은 고딕"

from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from pptx.oxml.ns import qn

P_BLUE = RGBColor(0x00, 0x66, 0xB3)
P_DARK = RGBColor(0x00, 0x4C, 0x86)
P_AI = RGBColor(0x6D, 0x4A, 0xFF)
P_GREY = RGBColor(0x34, 0x40, 0x54)
P_LIGHT = RGBColor(0xEF, 0xF6, 0xFC)
P_WHITE = RGBColor(0xFF, 0xFF, 0xFF)
P_SOFT = RGBColor(0xD9, 0xEA, 0xF7)
P_FOOT = RGBColor(0x66, 0x70, 0x85)
P_INK = RGBColor(0x10, 0x18, 0x28)

TITLE = "KINTEX AI 전시·행사시스템"


def pset(run, size, bold=False, color=P_GREY):
    f = run.font
    f.size = Pt(size)
    f.bold = bold
    f.color.rgb = color
    f.name = KOR_FONT
    rPr = run._r.get_or_add_rPr()
    for tag in ("a:ea", "a:cs"):
        el = rPr.find(qn(tag))
        if el is None:
            el = rPr.makeelement(qn(tag), {})
            rPr.append(el)
        el.set("typeface", KOR_FONT)


def add_box(slide, x, y, w, h):
    tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
    tb.text_frame.word_wrap = True
    return tb.text_frame


def add_rect(slide, x, y, w, h, fill, line=False):
    sh = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, Inches(x), Inches(y), Inches(w), Inches(h))
    sh.fill.solid()
    sh.fill.fore_color.rgb = fill
    if not line:
        sh.line.fill.background()
    sh.shadow.inherit = False
    return sh


def footer(slide, idx, total):
    tf = add_box(slide, 0.5, 7.05, 12.3, 0.35)
    r = tf.paragraphs[0].add_run()
    r.text = f"{TITLE} · AI 기능 고객 시연"
    pset(r, 9, color=P_FOOT)
    tf2 = add_box(slide, 12.3, 7.05, 0.7, 0.35)
    p2 = tf2.paragraphs[0]
    p2.alignment = PP_ALIGN.RIGHT
    r2 = p2.add_run()
    r2.text = f"{idx}/{total}"
    pset(r2, 9, color=P_FOOT)


def blank(prs):
    return prs.slides.add_slide(prs.slide_layouts[6])


def head(slide, kicker, title):
    add_rect(slide, 0, 0, 13.333, 0.18, P_BLUE)
    tf = add_box(slide, 0.55, 0.35, 12.2, 1.15)
    r = tf.paragraphs[0].add_run(); r.text = kicker; pset(r, 12, True, P_AI)
    p2 = tf.add_paragraph()
    r = p2.add_run(); r.text = title; pset(r, 25, True, P_DARK)
    add_rect(slide, 0.6, 1.62, 12.1, 0.03, P_SOFT)


def add_table(slide, x, y, w, header, rows, col_w=None, hdr_size=11, size=10.5, row_h=0.42):
    shp = slide.shapes.add_table(1 + len(rows), len(header), Inches(x), Inches(y),
                                 Inches(w), Inches(row_h * (1 + len(rows))))
    t = shp.table
    if col_w:
        for j, cw in enumerate(col_w):
            t.columns[j].width = Inches(cw)
    for j, htxt in enumerate(header):
        c = t.cell(0, j)
        c.fill.solid(); c.fill.fore_color.rgb = P_DARK
        c.vertical_anchor = MSO_ANCHOR.MIDDLE
        pa = c.text_frame.paragraphs[0]; pa.alignment = PP_ALIGN.CENTER
        r = pa.add_run(); r.text = htxt; pset(r, hdr_size, True, P_WHITE)
    for i, row in enumerate(rows):
        for j, val in enumerate(row):
            c = t.cell(i + 1, j)
            c.fill.solid()
            c.fill.fore_color.rgb = P_LIGHT if i % 2 == 0 else P_WHITE
            c.vertical_anchor = MSO_ANCHOR.MIDDLE
            c.margin_top = Pt(3); c.margin_bottom = Pt(3)
            lines = str(val).split("\n")
            for k, ln in enumerate(lines):
                pa = c.text_frame.paragraphs[0] if k == 0 else c.text_frame.add_paragraph()
                r = pa.add_run(); r.text = ln
                pset(r, size, j == 0, P_INK if j != 0 else P_DARK)
    return t


def note_bar(slide, y, text, h=0.55):
    add_rect(slide, 0.6, y, 12.1, h, P_LIGHT)
    tf = add_box(slide, 0.8, y + 0.06, 11.8, h - 0.1)
    r = tf.paragraphs[0].add_run(); r.text = text; pset(r, 12, True, P_DARK)


# ══════════════════════════════ 슬라이드 정의 ══════════════════════════════

DEMOS = [
    dict(
        no="DEMO 1", tag="공개 홈 · 로그인 불필요", time="5분",
        title="AI 관람 도우미",
        sub="자연어로 묻고, 실제 행사 데이터만 근거로 답한다",
        divider_points=[
            "관람객이 홈페이지에서 말하듯 질문 — 행사·일정·교통을 즉시 안내",
            "답변 근거는 실제 적재된 행사 DB — 근거가 없으면 \"모른다\"고 답변(환각 차단)",
        ],
        steps=[
            ("1", "공개 홈 접속 → 관람객 트랙 진입", "-", "관람객 서브홈 · AI 도우미 노출"),
            ("2", "AI 도우미에 질문 ①", "\"이번 달 킨텍스에서 볼만한 전시 알려줘\"", "행사명·기간·홀을 DB 근거로 요약 답변"),
            ("3", "질문 ② — 구체 카테고리", "\"유아용품 관련 박람회 언제 해?\"", "해당 카테고리 행사 목록 + 근거 표기"),
            ("4", "질문 ③ — 교통·편의", "\"대중교통으로 가는 방법 알려줘\"", "교통 가이드 데이터 기반 안내"),
            ("5", "환각 차단 확인 — 근거 없는 질문", "\"킨텍스에서 BTS 콘서트 언제 해?\"", "근거 없음 → 모른다고 답변 (추측 없음)"),
        ],
        point="핵심 포인트 — 모든 답변은 실제 행사 데이터 근거. 근거가 없으면 지어내지 않습니다.",
    ),
    dict(
        no="DEMO 2", tag="플로어플랜 스튜디오 · ★핵심", time="12분",
        title="자연어 AI 자동배치",
        sub="말로 시키면 배치가 나온다 — 자연어 → 3안 생성 → AI 평가 → 조감 렌더",
        divider_points=[
            "\"부스 120개, 프리미엄 3할…\" 한 문장 → AI가 조건 해석 → 배치안 3안 자동 생성",
            "배치안마다 활용률·통로폭 실계산 지표 근거의 AI 평가 요약",
            "선택한 배치안은 AI가 홀 전경(조감) 이미지로 렌더링",
        ],
        steps=[
            ("1", "시연 행사 → 부스 배치(플로어플랜 스튜디오) 진입", "-", "홀 평면 캔버스 표시"),
            ("2", "[AI 자동배치] → 자연어로 조건 입력 → [AI 해석]", "\"부스 120개, 프리미엄 3할, 무대 1개,\n입구 2개로 3안 뽑아줘\"", "AI가 조건 해석 → 폼 자동 채움 + 해석 근거 표시"),
            ("3", "해석된 값 확인(수정 가능) → [생성]", "목표 부스 수만 150으로 수정해 보기", "배치안 A/B/C 3안 생성 (제약 엔진)"),
            ("4", "배치안별 AI 평가 요약 확인", "-", "활용률·통로폭 등 실계산 지표 근거 장단점 요약"),
            ("5", "홀 전경(조감) 렌더 → 마음에 드는 안 [적용]", "(재시도) \"소형 부스 위주 200개, 라운지 2개\"", "AI 조감 이미지 생성 · 선택안 캔버스 반영"),
        ],
        point="핵심 포인트 — 수일 걸리던 CAD 배치 작업이 자연어 한 문장으로. AI 해석 결과는 폼에서 수정 가능해 사람이 최종 결정합니다.",
    ),
    dict(
        no="DEMO 3", tag="부스설계 스튜디오 · ★핵심", time="10분",
        title="부스 사진 시안 (AI)",
        sub="사진 한 장이 시공 시안이 된다 — 업로드 → 스타일 선택 → 시공 후 사진",
        divider_points=[
            "부스·공간 사진 한 장 업로드 → 스타일 선택 → AI가 '시공 후 결과 사진' 생성",
            "구조 보존 — 벽·바닥·기둥·구도는 그대로, 인테리어·조명·사이니지만 교체",
            "Before/After 슬라이더로 비교 — 장치공사 발주(견적) 자료로 바로 연결",
        ],
        steps=[
            ("1", "부스설계 스튜디오 → [사진 시안 (AI)] 섹션", "-", "01 업로드 → 02 스타일 → 03 지시 3단 UI"),
            ("2", "01 부스/공간 사진 업로드", "샘플 부스 사진 (드래그앤드롭)", "미리보기 표시"),
            ("3", "02 스타일 선택 + 03 자연어 지시(선택)", "스타일: \"테크\"\n지시: \"로고 월과 LED 사이니지, 우드 톤 바닥\"", "[사진 시안 생성] 활성화"),
            ("4", "[사진 시안 생성] 클릭", "-", "생성 진행 표시 (약 30~60초)"),
            ("5", "Before/After 슬라이더로 비교", "(재생성) 스타일 \"럭셔리\"로 비교", "구조·구도 보존, 인테리어만 교체된 시안 + AI 워터마크"),
        ],
        point="핵심 포인트 — 참가업체가 현장 사진만 보내면 공사 발주 전에 시공 결과를 미리 봅니다. 이 시안이 그대로 견적(옥션) 근거자료가 됩니다.",
    ),
    dict(
        no="DEMO 4", tag="경영분석 대시보드 · AI 콘솔", time="8분",
        title="자연어 데이터 조회 + 규정 챗",
        sub="데이터와 규정에 말로 묻는다 — 조회는 DB 실집계, 규정은 룰셋 근거",
        divider_points=[
            "\"이번 행사 부스 판매율은?\" — 자연어 질문을 DB 실조회로 답변",
            "\"천장 높이 제한은?\" — 등록된 규정 룰셋 버전을 근거 조항과 함께 답변",
        ],
        steps=[
            ("1", "좌측 메뉴 → 경영분석 → 대시보드 하단 AI 콘솔", "-", "NL Query / 규정 챗 탭"),
            ("2", "자연어 조회 ①", "\"이번 행사 부스 판매율 알려줘\"", "DB 실조회 수치·표 응답"),
            ("3", "자연어 조회 ②", "\"홀별 배정 현황 요약해줘\"", "홀 H1~H5 배정 집계 응답"),
            ("4", "규정 챗 ①", "\"부스 천장 높이 제한이 몇 미터야?\"", "룰셋(규정 버전) 근거 답변 + 근거 조항"),
            ("5", "규정 챗 ②", "\"전기 증설 신청은 언제까지 해야 해?\"", "규정·마일스톤 근거 답변"),
        ],
        point="핵심 포인트 — 답변 근거인 규정 룰셋 버전을 관리 화면에서 함께 확인할 수 있어, AI 답변의 출처가 투명합니다.",
    ),
]


def build():
    prs = Presentation()
    prs.slide_width = Inches(13.333)
    prs.slide_height = Inches(7.5)
    total = 4 + len(DEMOS) * 2  # 표지·개요·아젠다·클로징 + (간지+시나리오)×4

    idx = 1
    # ── 1. 표지 ──────────────────────────────────────────────
    s = blank(prs)
    add_rect(s, 0, 0, 13.333, 7.5, P_DARK)
    add_rect(s, 0, 4.9, 13.333, 0.06, P_AI)
    tf = add_box(s, 1.0, 2.0, 11.3, 2.6)
    r = tf.paragraphs[0].add_run(); r.text = TITLE; pset(r, 40, True, P_WHITE)
    p2 = tf.add_paragraph()
    r = p2.add_run(); r.text = "AI 기능 고객 시연"; pset(r, 26, True, P_SOFT)
    tf2 = add_box(s, 1.0, 5.1, 11.3, 1.6)
    r = tf2.paragraphs[0].add_run()
    r.text = "AI 관람 도우미 · 자연어 자동배치 · 부스 사진 시안 · 자연어 조회/규정 챗"
    pset(r, 15, True, P_WHITE)
    p2 = tf2.add_paragraph()
    r = p2.add_run(); r.text = "\"신청서를 내는 시간, 시공 후 사진을 먼저 본다\"  ·  2026. 7."
    pset(r, 13, False, P_SOFT)
    idx += 1

    # ── 2. 시연 개요 ─────────────────────────────────────────
    s = blank(prs)
    head(s, "OVERVIEW", "오늘 시연에서 보실 것")
    add_table(s, 0.6, 2.0, 12.1,
              ["구분", "무엇을 보여드리나", "핵심 가치"],
              [
                  ("데모 1", "AI 관람 도우미 — 자연어 질문 → 행사·교통 안내", "근거 기반 답변 · 환각 차단"),
                  ("데모 2 ★", "자연어 자동배치 — 한 문장 → 배치안 3안 + AI 평가 + 조감 렌더", "수일의 CAD 작업 → 수분"),
                  ("데모 3 ★", "부스 사진 시안 — 사진 한 장 → 시공 후 결과 사진", "발주 전 결과 확인 · 견적 근거"),
                  ("데모 4", "AI 콘솔 — 자연어 데이터 조회 + 규정 챗", "데이터·규정에 말로 질문"),
              ],
              col_w=[1.5, 7.3, 3.3], row_h=0.62)
    note_bar(s, 5.6,
             "3가지 핵심 메시지 — ① 말로 시키면 배치가 나온다  ② 사진 한 장이 시공 시안이 된다  ③ 모든 답변은 근거 기반",
             h=0.55)
    tf = add_box(s, 0.6, 6.35, 12.1, 0.5)
    r = tf.paragraphs[0].add_run()
    r.text = "시연 환경: 라이브 시스템(실서버·실데이터) — 화면 캡처가 아닌 실제 동작을 시연합니다."
    pset(r, 12, False, P_GREY)
    footer(s, idx, total); idx += 1

    # ── 3. 시연 순서 ─────────────────────────────────────────
    s = blank(prs)
    head(s, "AGENDA", "시연 순서 (약 40분)")
    add_table(s, 0.6, 2.0, 12.1,
              ["순서", "내용", "시간", "위치"],
              [
                  ("데모 1", "AI 관람 도우미 — 근거 기반 자연어 응답 (로그인 불필요)", "5분", "공개 홈"),
                  ("데모 2 ★", "자연어 AI 자동배치 → 3안 생성 → AI 평가 → 조감 렌더", "12분", "플로어플랜 스튜디오"),
                  ("데모 3 ★", "부스 사진 시안 — 사진 업로드 → 시공 후 사진 + Before/After", "10분", "부스설계 스튜디오"),
                  ("데모 4", "자연어 데이터 조회(NL Query) + 규정 챗", "8분", "경영분석 대시보드"),
                  ("마무리", "질의응답 · 안전 설계(폴백) 소개", "5분", "-"),
              ],
              col_w=[1.5, 7.3, 1.2, 2.1], row_h=0.6)
    note_bar(s, 5.75, "각 데모는 실제 화면에서 라이브로 진행하며, 본 자료는 데모 사이 브리지(안내) 용도입니다.", h=0.55)
    footer(s, idx, total); idx += 1

    # ── 4~11. 데모별 간지 + 시나리오 ────────────────────────
    for d in DEMOS:
        # 간지 (라이브 시연 직전 브리지)
        s = blank(prs)
        add_rect(s, 0, 0, 13.333, 7.5, P_BLUE)
        add_rect(s, 0, 5.9, 13.333, 0.05, P_AI)
        tf = add_box(s, 1.2, 1.6, 11.0, 3.6)
        r = tf.paragraphs[0].add_run()
        r.text = f"{d['no']}  ·  {d['tag']}  ·  {d['time']}"
        pset(r, 18, True, P_SOFT)
        p2 = tf.add_paragraph()
        r = p2.add_run(); r.text = d["title"]; pset(r, 40, True, P_WHITE)
        p3 = tf.add_paragraph()
        r = p3.add_run(); r.text = d["sub"]; pset(r, 17, False, P_SOFT)
        tfp = add_box(s, 1.25, 4.6, 10.9, 1.9)
        for k, pt in enumerate(d["divider_points"]):
            pa = tfp.paragraphs[0] if k == 0 else tfp.add_paragraph()
            pa.space_after = Pt(6)
            r = pa.add_run(); r.text = f"▎ {pt}"; pset(r, 14, False, P_WHITE)
        footer(s, idx, total); idx += 1

        # 시나리오 흐름 (진행자용 겸 고객 안내)
        s = blank(prs)
        head(s, f"{d['no']} · 시나리오", f"{d['title']} — 진행 흐름")
        add_table(s, 0.6, 1.95, 12.1,
                  ["단계", "진행", "입력(자연어) 예시", "보실 결과"],
                  d["steps"],
                  col_w=[0.7, 4.0, 3.9, 3.5], row_h=0.62, size=10)
        note_bar(s, 6.1, d["point"], h=0.6)
        footer(s, idx, total); idx += 1

    # ── 12. 클로징 ──────────────────────────────────────────
    s = blank(prs)
    add_rect(s, 0, 0, 13.333, 7.5, P_DARK)
    add_rect(s, 0, 1.9, 13.333, 0.05, P_AI)
    tf = add_box(s, 1.2, 1.0, 11.0, 0.8)
    r = tf.paragraphs[0].add_run(); r.text = "시연을 마치며 — 3가지 핵심 메시지"; pset(r, 28, True, P_WHITE)
    msgs = [
        ("① 말로 시키면 배치가 나온다", "자연어 → 제약 엔진 → 배치안 3안 + 실계산 지표 근거 AI 평가"),
        ("② 사진 한 장이 시공 시안이 된다", "구조 보존 image-to-image — 발주 전 결과 확인, 견적(옥션) 근거자료로 연결"),
        ("③ 모든 답변은 근거 기반", "행사 DB·규정 룰셋만 근거로 답변 — 근거가 없으면 모른다고 답한다(환각 차단)"),
    ]
    y = 2.5
    for t1, t2 in msgs:
        add_rect(s, 1.2, y, 10.9, 1.05, RGBColor(0x0A, 0x3A, 0x63))
        tf = add_box(s, 1.5, y + 0.12, 10.4, 0.9)
        r = tf.paragraphs[0].add_run(); r.text = t1; pset(r, 17, True, P_WHITE)
        p2 = tf.add_paragraph()
        r = p2.add_run(); r.text = t2; pset(r, 12.5, False, P_SOFT)
        y += 1.25
    tf = add_box(s, 1.2, 6.35, 10.9, 0.6)
    r = tf.paragraphs[0].add_run()
    r.text = "AI가 실패해도 화면은 멈추지 않습니다 — 모든 AI 기능은 수동 폴백을 갖춘 안전 설계  ·  AI 생성물 라벨·워터마크 표기"
    pset(r, 12, False, P_SOFT)
    idx += 1

    out = os.path.join(BASE, "AI_시연_시나리오.pptx")
    prs.save(out)
    print("saved:", out, "slides:", len(prs.slides))


if __name__ == "__main__":
    build()
