"""
スライド生成スターター（python-pptx 版）
=========================================

`sniffout-slide-design-guide.md` のデザイン定義を、そのままコードにしたものです。
ChatGPT のプロジェクトにこのファイルを入れておくと、AI はここにある関数を土台にして
PowerPoint を組み立てます。

このファイル単体でも動きます。
    pip install python-pptx
    python sniffout-pptx-starter.py
を実行すると、サンプルの 5 枚組デッキ `sample_deck.pptx` が生成されます。

自分のデザインにするときは「1. デザイン定義」のブロックだけを書き換えてください。
"""

import copy

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

# =====================================================================
# 1. デザイン定義（ここだけ書き換えれば、全スライドの見た目が変わる）
# =====================================================================

COLORS = {
    "navy":         RGBColor(0x0D, 0x1B, 0x4C),   # ラベル・見出し・主要コンテナ
    "navy_dark":    RGBColor(0x0A, 0x14, 0x38),   # フッターバー
    "blue":         RGBColor(0x25, 0x63, 0xEB),   # 強調キーワード
    "blue_light":   RGBColor(0xE8, 0xEF, 0xFD),   # 控えめな塗り
    "border":       RGBColor(0xC8, 0xCC, 0xD4),   # カード枠線
    "gray_light":   RGBColor(0xF5, 0xF6, 0xF8),   # 背景の塗り
    "gray_text":    RGBColor(0x6B, 0x72, 0x80),   # キャプション
    "white":        RGBColor(0xFF, 0xFF, 0xFF),
    "text":         RGBColor(0x1A, 0x1A, 0x2E),   # 本文
}

# ページタイトル左上のアクセントバー（紫 → ピンク → オレンジ）
GRADIENT = ("5931FD", "DA51E5", "EF8C67")

FONT_JA = "游ゴシック"   # 和文フォント
FONT_EN = "Arial"        # 英数字だけのブランド表記

FOOTER_TEXT = "© SNIFFOUT Inc."

SIZE = {
    "page_title":   Pt(12),   # ページタイトル（Bold）
    "section":      Pt(11),   # セクション見出し（Bold）
    "body":         Pt(9),    # 本文
    "label":        Pt(9),    # ネイビーラベル（Bold）
    "detail":       Pt(8),    # 詳細テキスト
    "caption":      Pt(7),    # キャプション・注記
    "cover_title":  Pt(24),   # 表紙タイトル
    "cover_sub":    Pt(11),   # 表紙サブタイトル
}

# スライド寸法とセーフエリア（単位はインチ）
PAGE_W, PAGE_H = 10.0, 5.625
MARGIN_L, MARGIN_R = 0.5, 0.5
CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R      # 9.0
CONTENT_TOP = 0.95                            # ページタイトルの下
CONTENT_BOTTOM = 5.05                         # これより下に要素を置かない
FOOTER_Y = 5.235

# 余白の定数
GAP_XS, GAP_SM, GAP_MD, GAP_LG = 0.08, 0.15, 0.25, 0.35
CARD_PADDING = 0.12


# =====================================================================
# 2. 共通部品（基本的に触らない）
# =====================================================================

def new_deck():
    """16:9・白背景のプレゼンテーションを作る"""
    prs = Presentation()
    prs.slide_width = Inches(PAGE_W)
    prs.slide_height = Inches(PAGE_H)
    return prs


def new_slide(prs):
    """白紙のスライドを1枚足す"""
    slide = prs.slides.add_slide(prs.slide_layouts[6])   # 6 = 白紙レイアウト
    bg = slide.background.fill
    bg.solid()
    bg.fore_color.rgb = COLORS["white"]
    return slide


def _apply_font(run, size, bold=False, color=None, font=FONT_JA):
    """和文フォントを確実に効かせる（latin と east-asian の両方を指定する）"""
    run.font.size = size
    run.font.bold = bold
    run.font.color.rgb = color if color is not None else COLORS["text"]
    run.font.name = font
    rPr = run._r.get_or_add_rPr()
    latin = rPr.find(qn("a:latin"))
    ea = rPr.find(qn("a:ea"))
    if ea is None:
        ea = rPr.makeelement(qn("a:ea"), {})
        if latin is not None:
            latin.addnext(ea)
        else:
            rPr.append(ea)
    ea.set("typeface", font)


def add_text(slide, text, x, y, w, h, size=None, bold=False, color=None,
             align=PP_ALIGN.LEFT, anchor=MSO_ANCHOR.TOP, font=FONT_JA,
             line_spacing=1.25):
    """テキストボックスを1つ置く。text に改行を含めると段落が分かれる"""
    box = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
    tf = box.text_frame
    tf.word_wrap = True
    tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = 0
    tf.vertical_anchor = anchor

    lines = str(text).split("\n")
    for i, line in enumerate(lines):
        p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
        p.alignment = align
        p.line_spacing = line_spacing
        run = p.add_run()
        run.text = line
        _apply_font(run, size or SIZE["body"], bold, color, font)
    return box


def _flatten(shape):
    """テーマ由来の影・効果を断ち、指定した塗りと線だけが出る状態にする"""
    shape.shadow.inherit = False
    style = shape._element.find(qn("p:style"))
    if style is not None:
        shape._element.remove(style)


def add_rect(slide, x, y, w, h, fill=None, line=None, rounded=False, line_w=1.0):
    """長方形（角丸も可）を1つ置く"""
    shape_type = MSO_SHAPE.ROUNDED_RECTANGLE if rounded else MSO_SHAPE.RECTANGLE
    shape = slide.shapes.add_shape(shape_type, Inches(x), Inches(y), Inches(w), Inches(h))
    if rounded:
        shape.adjustments[0] = 0.05          # 控えめな角丸
    if fill is None:
        shape.fill.background()
    else:
        shape.fill.solid()
        shape.fill.fore_color.rgb = fill
    if line is None:
        shape.line.fill.background()
    else:
        shape.line.color.rgb = line
        shape.line.width = Pt(line_w)
    _flatten(shape)
    return shape


def add_gradient_bar(slide, x, y, w, h, colors=GRADIENT, angle=45.0):
    """3色グラデーションのバー（python-pptx の標準APIは2色までのため XML を直接組む）"""
    shape = add_rect(slide, x, y, w, h, fill=COLORS["navy"])
    try:
        shape.fill.gradient()
        shape.fill.gradient_angle = angle
        grad = shape._element.spPr.find(qn("a:gradFill"))
        gs_lst = grad.find(qn("a:gsLst"))
        template = gs_lst[0]
        for gs in list(gs_lst):
            gs_lst.remove(gs)
        positions = [0, 50000, 100000][: len(colors)]
        for pos, hex_color in zip(positions, colors):
            gs = copy.deepcopy(template)
            gs.set("pos", str(pos))
            for child in list(gs):
                gs.remove(child)
            gs.append(gs.makeelement(qn("a:srgbClr"), {"val": hex_color}))
            gs_lst.append(gs)
    except Exception:
        pass          # グラデーションが作れない環境では、濃紺の単色バーのまま使う
    return shape


def add_title_block(slide, title):
    """左上のページタイトル（グラデーションバー＋タイトル文字）※表紙には置かない"""
    add_gradient_bar(slide, 0, 0, 0.105, 0.551)
    add_text(slide, title, 0.42, 0.10, 8.0, 0.35,
             size=SIZE["page_title"], bold=True, color=COLORS["text"],
             anchor=MSO_ANCHOR.MIDDLE)


def add_footer(slide, page_no=None):
    """全スライド共通のフッター（ネイビーの無地バー。上にアクセント線は入れない）"""
    add_rect(slide, 0, FOOTER_Y, PAGE_W, 0.39, fill=COLORS["navy_dark"])
    add_text(slide, FOOTER_TEXT, 0.3, FOOTER_Y + 0.045, 3.0, 0.25,
             size=SIZE["caption"], color=COLORS["white"], font=FONT_EN)
    if page_no is not None:
        add_text(slide, str(page_no), PAGE_W - 0.8, FOOTER_Y + 0.045, 0.5, 0.25,
                 size=SIZE["detail"], color=COLORS["white"],
                 align=PP_ALIGN.RIGHT, font=FONT_EN)


def add_navy_label(slide, text, x, y, w=1.2, h=0.4):
    """カテゴリ名・ステップ番号に使う濃紺のラベルボックス"""
    add_rect(slide, x, y, w, h, fill=COLORS["navy"], rounded=True)
    add_text(slide, text, x, y, w, h, size=SIZE["label"], bold=True,
             color=COLORS["white"], align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)


def add_card(slide, x, y, w, h, title=None, body=None, fill=None):
    """枠線つきのコンテンツカード"""
    add_rect(slide, x, y, w, h,
             fill=fill or COLORS["white"], line=COLORS["border"], rounded=True)
    inner_y = y + CARD_PADDING
    if title:
        add_text(slide, title, x + CARD_PADDING, inner_y, w - CARD_PADDING * 2, 0.26,
                 size=SIZE["label"], bold=True, color=COLORS["navy"])
        inner_y += 0.30
    if body:
        add_text(slide, body, x + CARD_PADDING, inner_y, w - CARD_PADDING * 2,
                 y + h - inner_y - CARD_PADDING, size=SIZE["detail"])


def add_triangle_marker(slide, x, y, direction="right", w=0.30, h=0.11):
    """分岐のない一方通行フローに置くマーカー（矢印は使わない）"""
    shape = slide.shapes.add_shape(MSO_SHAPE.ISOSCELES_TRIANGLE,
                                   Inches(x), Inches(y), Inches(w), Inches(h))
    shape.fill.solid()
    shape.fill.fore_color.rgb = COLORS["border"]
    shape.line.fill.background()
    _flatten(shape)
    shape.rotation = {"up": 0, "right": 90, "down": 180, "left": 270}[direction]
    return shape


def add_branch_connector(slide, parent_cx, parent_bottom, child_cxs, child_top,
                         color=None, width=1.0):
    """分岐のあるフローに使うカギ線コネクタ（幹線→分配線→引き込み線）"""
    color = color or COLORS["border"]
    bus_y = (parent_bottom + child_top) / 2

    def line(x1, y1, x2, y2, arrow=False):
        conn = slide.shapes.add_connector(1, Inches(x1), Inches(y1), Inches(x2), Inches(y2))
        conn.line.color.rgb = color
        conn.line.width = Pt(width)
        if arrow:
            ln = conn.line._get_or_add_ln()
            tail = ln.makeelement(qn("a:tailEnd"), {"type": "triangle", "w": "med", "len": "med"})
            ln.append(tail)
        return conn

    line(parent_cx, parent_bottom, parent_cx, bus_y)                       # 幹線
    line(min(child_cxs), bus_y, max(child_cxs), bus_y)                     # 分配線
    for cx in child_cxs:                                                   # 引き込み線
        line(cx, bus_y, cx, child_top, arrow=True)


def content_slide(prs, title, page_no):
    """ページタイトルとフッターまで入った、書き始められる状態のスライドを返す"""
    slide = new_slide(prs)
    add_title_block(slide, title)
    add_footer(slide, page_no)
    return slide


# =====================================================================
# 3. サンプル（ここを自分の資料の中身に差し替える）
# =====================================================================

def build_sample(path="sample_deck.pptx"):
    prs = new_deck()

    # --- 表紙（ページタイトルは置かない） ---
    slide = new_slide(prs)
    add_rect(slide, MARGIN_L, 2.05, 1.6, 0.035, fill=COLORS["blue"])
    add_text(slide, "調査レポート作成の自動化", MARGIN_L, 2.25, 8.5, 0.6,
             size=SIZE["cover_title"], bold=True, color=COLORS["navy"])
    add_text(slide, "2026.09.01 ／ 株式会社スニフアウト", MARGIN_L, 3.0, 6.0, 0.3,
             size=SIZE["cover_sub"], color=COLORS["gray_text"])
    add_footer(slide)

    # --- パターンA：サマリ ---
    slide = content_slide(prs, "ご提案サマリ", 1)
    add_text(slide, "定型レポートの作成工程を自動化し、月40時間を3時間に短縮する",
             MARGIN_L, CONTENT_TOP, CONTENT_W, 0.3, size=SIZE["body"], color=COLORS["gray_text"])
    rows = [
        ("目的", "調査レポートの作成時間を削減し、分析に時間を振り向ける"),
        ("対象", "月次で発行している定型レポート 3 種"),
        ("期間", "2026年10月 〜 2026年12月（3ヶ月）"),
        ("体制", "調査1部 2名 ／ 情報システム部 1名"),
    ]
    y = CONTENT_TOP + 0.45
    for label, body in rows:
        add_navy_label(slide, label, MARGIN_L, y, w=1.1, h=0.5)
        add_card(slide, MARGIN_L + 1.1 + GAP_XS, y, CONTENT_W - 1.1 - GAP_XS, 0.5, body=body)
        y += 0.5 + GAP_SM

    # --- パターンB：Before / After ---
    slide = content_slide(prs, "導入前後の比較", 2)
    add_text(slide, "同じ作業を、同じ品質で、10分の1の時間で終える",
             MARGIN_L, CONTENT_TOP, CONTENT_W, 0.3, size=SIZE["body"], color=COLORS["gray_text"])
    cols = [("時間", "月 40 時間", "月 3 時間"),
            ("品質", "転記ミスが毎回発生", "転記ミスはゼロ"),
            ("横展開", "担当者ごとに書式が違う", "全社共通の書式")]
    card_w = (CONTENT_W - 1.1 - GAP_XS - GAP_SM * 2) / 3
    top_y, bottom_y = CONTENT_TOP + 0.45, CONTENT_TOP + 1.75
    add_navy_label(slide, "Before", MARGIN_L, top_y, w=1.1, h=0.9)
    add_navy_label(slide, "After", MARGIN_L, bottom_y, w=1.1, h=0.9)
    for i, (name, before, after) in enumerate(cols):
        x = MARGIN_L + 1.1 + GAP_XS + i * (card_w + GAP_SM)
        add_card(slide, x, top_y, card_w, 0.9, title=name, body=before)
        add_card(slide, x, bottom_y, card_w, 0.9, title=name, body=after)
        add_triangle_marker(slide, x + card_w / 2 - 0.15, top_y + 1.0, "down")

    # --- パターンC：ステップフロー（分岐あり） ---
    slide = content_slide(prs, "処理の流れ", 3)
    add_text(slide, "集計シートを更新するだけで、3種類のレポートが同時に出来上がる",
             MARGIN_L, CONTENT_TOP, CONTENT_W, 0.3, size=SIZE["body"], color=COLORS["gray_text"])
    parent_x, parent_y, parent_w, parent_h = 3.9, CONTENT_TOP + 0.5, 2.2, 0.55
    add_card(slide, parent_x, parent_y, parent_w, parent_h, title="集計シートを更新")
    child_w = 2.4
    child_y = parent_y + 1.5
    child_xs = [MARGIN_L + i * (child_w + GAP_SM) for i in range(3)]
    for x, name in zip(child_xs, ["月次レポート", "部門別サマリ", "経営会議資料"]):
        add_card(slide, x, child_y, child_w, 0.75, title=name, body="自動生成")
    add_branch_connector(slide, parent_x + parent_w / 2, parent_y + parent_h,
                         [x + child_w / 2 for x in child_xs], child_y)

    # --- パターンE：見積り ---
    slide = content_slide(prs, "お見積り", 4)
    header_y = CONTENT_TOP + 0.2
    widths = [3.2, 4.0, 1.8]
    heads = ["項目", "内容", "金額（税抜）"]
    x = MARGIN_L
    for w, head in zip(widths, heads):
        add_rect(slide, x, header_y, w, 0.36, fill=COLORS["navy"])
        add_text(slide, head, x + 0.12, header_y, w - 0.24, 0.36, size=SIZE["label"],
                 bold=True, color=COLORS["white"], anchor=MSO_ANCHOR.MIDDLE)
        x += w
    body_rows = [("初期構築", "要件定義・テンプレート実装・テスト", "1,800,000"),
                 ("運用支援", "月次レビュー・改修対応（3ヶ月）", "450,000")]
    y = header_y + 0.36
    for item, detail, amount in body_rows:
        x = MARGIN_L
        for w, value, align in zip(widths, (item, detail, amount),
                                   (PP_ALIGN.LEFT, PP_ALIGN.LEFT, PP_ALIGN.RIGHT)):
            add_rect(slide, x, y, w, 0.42, fill=COLORS["white"], line=COLORS["border"])
            add_text(slide, value, x + 0.12, y, w - 0.24, 0.42, size=SIZE["body"],
                     align=align, anchor=MSO_ANCHOR.MIDDLE)
            x += w
        y += 0.42
    x = MARGIN_L
    for w, value, align in zip(widths, ("合計", "", "2,250,000"),
                               (PP_ALIGN.LEFT, PP_ALIGN.LEFT, PP_ALIGN.RIGHT)):
        add_rect(slide, x, y, w, 0.42, fill=COLORS["blue_light"], line=COLORS["border"])
        add_text(slide, value, x + 0.12, y, w - 0.24, 0.42, size=SIZE["body"],
                 bold=True, color=COLORS["navy"], align=align, anchor=MSO_ANCHOR.MIDDLE)
        x += w
    add_text(slide, "※ 表示金額は税抜きです。交通費・宿泊費は実費精算とさせていただきます",
             MARGIN_L, y + 0.55, CONTENT_W, 0.25, size=SIZE["caption"], color=COLORS["gray_text"])

    prs.save(path)
    return path


if __name__ == "__main__":
    print("生成しました:", build_sample())
