# -*- coding: utf-8 -*-
"""
make_outline.py

기능:
- output_context/context/domain_prompt.txt
- output_context/context/corpus.txt
을 기반으로 목차를 생성하고,

1) outline_issue_report.txt 저장
2) outline_issue_report.html 저장 (테스트.html 레이아웃 기반 표 형태)
"""

import os
import sys
import re
from pathlib import Path
from datetime import datetime
from typing import List, Dict, Any, Tuple

from openai import OpenAI
from api_config import API_KEYS

# ===== 경로 설정 =====
DATA_ROOT   = Path(r"D:\for python\geulbeot-light\geulbeot-light\00.test\hwpx\out")
OUTPUT_ROOT = Path(r"D:\for python\geulbeot-light\geulbeot-light\00.test\hwpx\out\out")   # 출력 위치
CONTEXT_DIR = OUTPUT_ROOT / "context"
LOG_DIR     = OUTPUT_ROOT / "logs"

for d in [CONTEXT_DIR, LOG_DIR]:
    d.mkdir(parents=True, exist_ok=True)

# ===== OpenAI 설정 (구조 유지) =====
OPENAI_API_KEY = API_KEYS.get('GPT_API_KEY', '')
GPT_MODEL      = "gpt-5-2025-08-07"

client = OpenAI(api_key=OPENAI_API_KEY)

# ===== 목차 파싱용 정규식 보완 (5분할 대응) =====
RE_KEYWORDS = re.compile(r"(#\S+)")
RE_L1 = re.compile(r"^\s*(\d+)\.\s+(.+?)\s*$")
RE_L2 = re.compile(r"^\s*(\d+\.\d+)\s+(.+?)\s*$")
RE_L3 = re.compile(r"^\s*(\d+\.\d+\.\d+)\s+(.+?)\s*$")

def log(msg: str):
    print(msg, flush=True)
    with (LOG_DIR / "make_outline_log.txt").open("a", encoding="utf-8") as f:
        f.write(msg + "\n")

def load_domain_prompt() -> str:
    p = CONTEXT_DIR / "domain_prompt.txt"
    if not p.exists():
        log("domain_prompt.txt가 없습니다. 먼저 domain_prompt.py를 실행해야 합니다.")
        sys.exit(1)
    return p.read_text(encoding="utf-8", errors="ignore").strip()

def load_corpus() -> str:
    p = CONTEXT_DIR / "corpus.txt"
    if not p.exists():
        log("corpus.txt가 없습니다. 먼저 make_corpus.py를 실행해야 합니다.")
        sys.exit(1)
    return p.read_text(encoding="utf-8", errors="ignore").strip()


# 기존 RE_L1, RE_L2는 유지하고 아래 두 개를 추가/교체합니다.
RE_L3_HEAD = re.compile(r"^\s*(\d+\.\d+\.\d+)\s+(.+)$") 
RE_L3_TOPIC = re.compile(r"^\s*[\-\*]\s+(.+?)\s*\|\s*(.+?)\s*\|\s*(\[.+?\])\s*\|\s*(.+)$") 

def generate_outline(domain_prompt: str, corpus: str) -> str:
    sys_msg = {
        "role": "system",
        "content": (
            domain_prompt + "\n\n"
            "너는 건설/측량 DX 기술 보고서의 구조를 설계하는 시니어 기술사이다. "
            "주어진 corpus를 분석하여, 실무자가 즉시 활용 가능한 고밀도 지침서 목차를 설계하라."
        ),
    }

    user_msg = {
        "role": "user",
        "content": f"""
아래 [corpus]를 바탕으로 보고서 제목과 전략적 목차를 설계하라.

[corpus]
{corpus}

요구 사항:
1) 첫 줄에 보고서 제목 1개를 작성하라.
2) 그 아래 목차를 번호 기반 계측 구조로 작성하라.
   - 대목차: 1. / 2. / 3. ...
   - 중목차: 1.1 / 1.2 / ...
   - 소목차: 1.1.1 / 1.1.2 / ...
3) **수량 제약 (중요)**:
   - 대목차(1.)는 5~8개로 구성하라.
   - **중목차(1.1) 하나당 소목차(1.1.1, 1.1.2...)는 반드시 2개에서 4개 사이로 구성하라.** (절대 1개만 만들지 말 것)
   - 소목차(1.1.1) 하나당 '핵심주제(꼭지)'는 반드시 2개에서 3개 사이로 구성하라.

[소목차 작성 형식]
1.1.1 소목차 제목 
 - 핵심주제 1 | #키워드 | [유형] | 집필가이드(데이터/표 구성 지침) 
 - 핵심주제 2 | #키워드 | [유형] | 집필가이드(데이터/표 구성 지침)

5) [유형] 분류 가이드:
   - [비교형]: 기존 vs DX 방식의 비교표(Table)가 필수적인 경우
   - [기술형]: RMSE, GSD, 중복도 등 정밀 수치와 사양 설명이 핵심인 경우
   - [절차형]: 단계별 워크플로 및 체크리스트가 중심인 경우
   - [인사이트형]: 한계점 분석 및 전문가 제언(☞)이 중심인 경우
6) 집필가이드는 50자 내외로, "어떤 데이터를 검색해서 어떤 표를 그려라"와 같이 구체적으로 지시하라.
7) 대목차는 최대 8개 이내로 구성하라.
"""
    }
    resp = client.chat.completions.create(
        model=GPT_MODEL,
        messages=[sys_msg, user_msg],
    )
    return (resp.choices[0].message.content or "").strip()



def parse_outline(outline_text: str) -> Tuple[str, List[Dict[str, Any]]]:
    lines = [ln.rstrip() for ln in outline_text.splitlines() if ln.strip()]
    if not lines: return "", []

    title = lines[0].strip() # 첫 줄은 보고서 제목
    rows = []
    current_section = None # 현재 처리 중인 소목차(1.1.1)를 추적

    for ln in lines[1:]:
        raw = ln.strip()
        
        # 1. 소목차 헤더(1.1.1 제목) 발견 시
        m3_head = RE_L3_HEAD.match(raw)
        if m3_head:
            num, s_title = m3_head.groups()
            current_section = {
                "depth": 3, 
                "num": num, 
                "title": s_title,
                "sub_topics": [] # 여기에 아래 줄의 꼭지들을 담을 예정
            }
            rows.append(current_section)
            continue
            
        # 2. 세부 꼭지(- 주제 | #키워드 | [유형] | 가이드) 발견 시
        m_topic = RE_L3_TOPIC.match(raw)
        if m_topic and current_section:
            t_title, kws_raw, t_type, guide = m_topic.groups()
            # 키워드 추출 (#키워드 형태)
            kws = [k.lstrip("#").strip() for k in RE_KEYWORDS.findall(kws_raw)]
            
            # 현재 소목차(current_section)의 리스트에 추가
            current_section["sub_topics"].append({
                "topic_title": t_title,
                "keywords": kws,
                "type": t_type,
                "guide": guide
            })
            continue

        # 3. 대목차(1.) 처리
        m1 = RE_L1.match(raw)
        if m1:
            rows.append({"depth": 1, "num": m1.group(1).strip(), "title": m1.group(2).strip()})
            current_section = None # 소목차 구간 종료
            continue

        # 4. 중목차(1.1) 처리
        m2 = RE_L2.match(raw)
        if m2:
            rows.append({"depth": 2, "num": m2.group(1).strip(), "title": m2.group(2).strip()})
            current_section = None # 소목차 구간 종료
            continue

    return title, rows

def html_escape(s: str) -> str:
    s = s or ""
    return (s.replace("&", "&amp;")
             .replace("<", "&lt;")
             .replace(">", "&gt;")
             .replace('"', "&quot;")
             .replace("'", "&#39;"))

def chunk_rows(rows: List[Dict[str, Any]], max_rows_per_page: int = 26) -> List[List[Dict[str, Any]]]:
    """
    A4 1장에 표가 길어지면 넘치므로, 단순 행 개수로 페이지 분할한다.
    """
    out = []
    cur = []
    for r in rows:
        cur.append(r)
        if len(cur) >= max_rows_per_page:
            out.append(cur)
            cur = []
    if cur:
        out.append(cur)
    return out

def build_outline_table_html(rows: List[Dict[str, Any]]) -> str:
    """
    테스트.html의 table 스타일을 그대로 쓰는 전제의 표 HTML
    """
    head = """
    <table>
        <thead>
            <tr>
                <th>구분</th>
                <th>번호</th>
                <th>제목</th>
                <th>키워드</th>
            </tr>
        </thead>
        <tbody>
    """

    body_parts = []
    for r in rows:
        depth = r["depth"]
        num = html_escape(r["num"])
        title = html_escape(r["title"])
        kw = " ".join([f"#{k}" for k in r.get("keywords", []) if k])
        kw = html_escape(kw)

        if depth == 1:
            body_parts.append(
                f"""
                <tr>
                    <td class="group-cell">대목차</td>
                    <td>{num}</td>
                    <td>{title}</td>
                    <td></td>
                </tr>
                """
            )
        elif depth == 2:
            body_parts.append(
                f"""
                <tr>
                    <td class="group-cell">중목차</td>
                    <td>{num}</td>
                    <td>{title}</td>
                    <td></td>
                </tr>
                """
            )
        else:
            body_parts.append(
                f"""
                <tr>
                    <td class="group-cell">소목차</td>
                    <td>{num}</td>
                    <td>{title}</td>
                    <td>{kw}</td>
                </tr>
                """
            )

    tail = """
        </tbody>
    </table>
    """
    return head + "\n".join(body_parts) + tail

def build_outline_html(report_title: str, rows: List[Dict[str, Any]]) -> str:
    """
    테스트.html 레이아웃 구조를 그대로 따라 A4 시트 형태로 HTML 생성
    """
    css = r"""
        @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700;900&display=swap');

        :root {
            --primary-blue: #3057B9;
            --gray-light: #F2F2F2;
            --gray-medium: #E6E6E6;
            --gray-dark: #666666;
            --border-light: #DDDDDD;
            --text-black: #000000;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-print-color-adjust: exact;
        }

        body {
            font-family: 'Noto Sans KR', sans-serif;
            background-color: #f0f0f0;
            color: var(--text-black);
            line-height: 1.35;
            display: flex;
            justify-content: center;
            padding: 10px 0;
        }

        .sheet {
            background-color: white;
            width: 210mm;
            height: 297mm;
            padding: 20mm 20mm;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            position: relative;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            margin-bottom: 12px;
        }

        @media print {
            body { background: none; padding: 0; }
            .sheet { box-shadow: none; margin: 0; border: none; page-break-after: always; }
        }

        .page-header {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            margin-bottom: 15px;
            font-size: 8.5pt;
            color: var(--gray-dark);
        }

        .header-title {
            font-size: 24pt;
            font-weight: 900;
            margin-bottom: 8px;
            letter-spacing: -1.5px;
            color: #111;
        }

        .title-divider {
            height: 4px;
            background-color: var(--primary-blue);
            width: 100%;
            margin-bottom: 20px;
        }

        .lead-box {
            background-color: var(--gray-light);
            padding: 18px 20px;
            margin-bottom: 5px;
            border-radius: 2px;
            text-align: center;
        }

        .lead-box div {
            font-size: 13pt;
            font-weight: 700;
            color: var(--primary-blue);
            letter-spacing: -0.5px;
        }

        .lead-notes {
            font-size: 8.5pt;
            color: #777;
            margin-bottom: 20px;
            padding-left: 5px;
            text-align: right;
        }

        .body-content { flex: 1; }

        .section { margin-bottom: 22px; }

        .section-title {
            font-size: 13pt;
            font-weight: 700;
            display: flex;
            align-items: center;
            margin-bottom: 10px;
            color: #111;
        }

        .section-title::before {
            content: "";
            display: inline-block;
            width: 10px;
            height: 10px;
            background-color: #999;
            margin-right: 10px;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            margin: 8px 0;
            font-size: 9.5pt;
            border-top: 1.5px solid #333;
        }

        th {
            background-color: var(--gray-medium);
            font-weight: 700;
            padding: 10px;
            border: 1px solid var(--border-light);
        }

        td {
            padding: 10px;
            border: 1px solid var(--border-light);
            vertical-align: middle;
        }

        .group-cell {
            background-color: #F9F9F9;
            font-weight: 700;
            width: 16%;
            text-align: center;
            color: var(--primary-blue);
            white-space: nowrap;
        }

        .page-footer {
            margin-top: 15px;
            padding-top: 10px;
            display: flex;
            justify-content: space-between;
            font-size: 8.5pt;
            color: var(--gray-dark);
            border-top: 1px solid #EEE;
        }

        .footer-page { flex: 1; text-align: center; }
    """

    pages = chunk_rows(rows, max_rows_per_page=26)

    html_pages = []
    total_pages = len(pages) if pages else 1
    for i, page_rows in enumerate(pages, start=1):
        table_html = build_outline_table_html(page_rows)

        html_pages.append(f"""
        <div class="sheet">
            <header class="page-header">
                <div class="header-left">
                    보고서: 목차 자동 생성 결과
                </div>
                <div class="header-right">
                    작성일자: {datetime.now().strftime("%Y. %m. %d.")}
                </div>
            </header>

            <div class="title-block">
                <h1 class="header-title">{html_escape(report_title)}</h1>
                <div class="title-divider"></div>
            </div>

            <div class="body-content">
                <div class="lead-box">
                    <div>확정 목차 표 형태 정리본</div>
                </div>
                <div class="lead-notes">목차는 outline_issue_report.txt를 기반으로 표로 재구성됨</div>

                <div class="section">
                    <div class="section-title">목차</div>
                    {table_html}
                </div>
            </div>

            <footer class="page-footer">
                <div class="footer-slogan">Word Style v2 Outline</div>
                <div class="footer-page">- {i} / {total_pages} -</div>
                <div class="footer-info">outline_issue_report.html</div>
            </footer>
        </div>
        """)

    return f"""<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>{html_escape(report_title)} - Outline</title>
  <style>{css}</style>
</head>
<body>
  {''.join(html_pages)}
</body>
</html>
"""

def main():
    log("=== 목차 생성 시작 ===")
    domain_prompt = load_domain_prompt()
    corpus        = load_corpus()

    outline = generate_outline(domain_prompt, corpus)

    # TXT 저장 유지
    out_txt = CONTEXT_DIR / "outline_issue_report.txt"
    out_txt.write_text(outline, encoding="utf-8")
    log(f"목차 TXT 저장 완료: {out_txt}")

    # HTML 추가 저장
    title, rows = parse_outline(outline)
    out_html = CONTEXT_DIR / "outline_issue_report.html"
    out_html.write_text(build_outline_html(title, rows), encoding="utf-8")
    log(f"목차 HTML 저장 완료: {out_html}")

    log("=== 목차 생성 종료 ===")

if __name__ == "__main__":
    main()
