# -*- coding: utf-8 -*-
from dotenv import load_dotenv
load_dotenv()

"""
domain_api.py


  硫由 API + 
                  
ъ⑸ (app.py):
    from domain_api import register_domain_routes
    register_domain_routes(app)
"""

import os
import json
from pathlib import Path
from flask import request, jsonify
import threading
import hashlib
import psycopg2
from db import get_conn
from db import get_conn

# ===== 寃쎈 
               ㅼ =====
BASE_DIR = Path(__file__).parent
DOMAIN_CONFIG_PATH = BASE_DIR / "domain_config.json"
DOMAIN_DIR = BASE_DIR / "domain"
# 
     寃쎈 (step3~9媛 ъ⑺ 寃쎈)
PIPELINE_OUTPUT_ROOT = Path(os.getenv("PIPELINE_OUTPUT_ROOT", "/tmp/pipeline_output"))
CONTEXT_DIR = PIPELINE_OUTPUT_ROOT / "context"
pipeline_jobs = {}
def register_domain_routes(app):
    """Flask 깆
                 硫 고 깅
    @app.route('/api/domain-config', methods=['GET'])
    def get_domain_config():
        """
             硫
                ㅼ 諛 
                         硫 щ 泥댄
                for cat in config.get('categories', []):
                    if cat.get('file'):
                        fpath = DOMAIN_DIR / cat['file']
                        cat['file_exists'] = fpath.exists()
                        cat['file_size'] = fpath.stat().st_size if fpath.exists() else 0

                    for child in cat.get('children', []):
                        if child.get('file'):
                            fpath = DOMAIN_DIR / child['file']
                            child['file_exists'] = fpath.exists()
                            child['file_size'] = fpath.stat().st_size if fpath.exists() else 0

                return jsonify(config)
            else:
                return jsonify({'error': 'domain_config.json not found', 'categories': []}), 404
        except Exception as e:
            return jsonify({'error': str(e), 'categories': []}), 500


    @app.route('/api/domain-combine', methods=['POST'])
    def combine_domains():
        """
        
          
              硫㼼
                     ⑹
                         domain_prompt.txt濡 

        泥:
            { "selected": ["civil_general", "survey", "bim"] }

        :
            { "success": true, "combined_length": 3200, "selected_names": [...] }
        """
        try:
            data = request.get_json()
            selected_ids = data.get('selected', [])

            if not selected_ids:
                return jsonify({
                    'success': True,
                    'combined_length': 0,
                    'selected_names': [],
                    'message': '
                                  - step3  遺
                                                  
                                                   '
                })

            # config 濡
            config = json.loads(DOMAIN_CONFIG_PATH.read_text(encoding='utf-8'))

            # 
                ID   + 
                              留ㅽ
            domain_parts = []
            guide_parts = []
            selected_names = []

            for cat in config.get('categories', []):
                is_guide = (cat['id'] == 'report_guide')
                target = guide_parts if is_guide else domain_parts

                if cat['id'] in selected_ids and cat.get('file'):
                    fpath = DOMAIN_DIR / cat['file']
                    if fpath.exists():
                        content = fpath.read_text(encoding='utf-8', errors='ignore').strip()
                        if content:
                            target.append(f"[{cat['label']}]\n{content}")
                            selected_names.append(cat['label'])

                for child in cat.get('children', []):
                    if child['id'] in selected_ids and child.get('file'):
                        fpath = DOMAIN_DIR / child['file']
                        if fpath.exists():
                            content = fpath.read_text(encoding='utf-8', errors='ignore').strip()
                            if content:
                                target.append(f"[{child['label']}]\n{content}")
                                selected_names.append(child['label'])
                                selected_names.append(child['label'])

            if not domain_parts and not guide_parts:
                return jsonify({
                    'success': False,
                    'error': '
                                
                                    硫
                                         댁듬.'
                })

            sep = "\n\n" + "=" * 50 + "\n\n"
            sections = []

            if domain_parts:
                domain_names = [n for n in selected_names if n not in ['紐⑹감 援ъ
                                                                                  媛', '蹂닿
                                                                                                 臾몄껜 媛']]
                sections.append(
                    f"
                        ㅼ 遺
                               쇱
                                  臾멸: {', '.join(domain_names)}.\n"
                    f"ㅼ
                           硫
                                湲곕쇰, ъㅼ쇨굅 
                                                      臾몄 댁⑹
                                                                    
                                                                      깊痢≪쎌 湲吏, 怨듬 洹쇨굅 몄
                                                                                                         理 蹂댁〈
                                                                                                                     硫
                                                                                                                        臾 吏
                                                                                                                                
                                                                                                                                   媛]\n"
                    f"ㅼ 媛瑜 李멸 蹂닿
                                               ⑹감 援ъ
                                                       깃낵 臾몄껜瑜 寃곗   二쇱
 (ъ⑹
     
       닿
             
                媛]\n"
                        "ㅼ 媛瑜 李멸 蹂닿
                                                  ⑹감 援ъ
                                                          깃낵 臾몄껜瑜 寃곗 
            CONTEXT_DIR.mkdir(parents=True, exist_ok=True)
            output_path = CONTEXT_DIR / "domain_prompt.txt"
            output_path.write_text(final_text, encoding='utf-8')

            return jsonify({
                'success': True,
                'combined_length': len(final_text),
                'selected_names': selected_names,
                'selected_ids': selected_ids,
                'output_path': str(output_path)
            })

        except Exception as e:
            return jsonify({'success': False, 'error': str(e)}), 500


    @app.route('/api/domain-list', methods=['GET'])
    def list_domain_files():
        """
        domains/ 대
                        硫由ъ
        """
        try:
            files = []

            if DOMAIN_DIR.exists():
                for f in sorted(DOMAIN_DIR.rglob('*.txt')):
                    rel = f.relative_to(DOMAIN_DIR)
                    files.append({
                        'path': str(rel),
                        'name': f.stem,
                        'size': f.stat().st_size,
                        'preview': f.read_text(encoding='utf-8', errors='ignore')[:200]
                    })

            return jsonify({
                'success': True,
                'files': files,
                'domains_dir': str(DOMAIN_DIR)
            })

        except Exception as e:
            return jsonify({'success': False, 'error': str(e)}), 500


    @app.route('/api/domain-save', methods=['POST'])
    def save_domain_file():
        """
        
          硫/

        泥:
            { "id": "survey", "content": "痢〓 遺
                                                 쇱
                                                    臾 吏 content媛 
                                                                       ⑸.'})

            # config
                       李얘린
            config = json.loads(DOMAIN_CONFIG_PATH.read_text(encoding='utf-8'))
            file_path = None

            for cat in config.get('categories', []):
                if cat['id'] == domain_id:
                    file_path = cat.get('file')
                    break
                for child in cat.get('children', []):
                    if child['id'] == domain_id:
                        file_path = child.get('file')
                        break
                if file_path:
                    break

            if not file_path:
                return jsonify({'success': False, 'error': f'
                                                               硫
                                                                   듬: {domain_id}'})

            # 
            full_path = BASE_DIR / file_path
            full_path.parent.mkdir(parents=True, exist_ok=True)
            full_path.write_text(content, encoding='utf-8')

            return jsonify({
                'success': True,
                'path': str(full_path),
                'size': len(content)
            })

        except Exception as e:
            return jsonify({'success': False, 'error': str(e)}), 500


    @app.route('/api/pipeline/status', methods=['GET'])
    def pipeline_status():
        """
               step  щ"""
        try:
            status = {
                'step3_domain': (CONTEXT_DIR / 'domain_prompt.txt').exists(),
                'step4_chunks': len(list((PIPELINE_OUTPUT_ROOT / 'rag').glob('*_chunks.json'))) if (PIPELINE_OUTPUT_ROOT / 'rag').exists() else 0,
                'step5_faiss': (PIPELINE_OUTPUT_ROOT / 'rag' / 'faiss.index').exists(),
                'step6_corpus': (CONTEXT_DIR / 'corpus.txt').exists(),
                'step7_outline': (CONTEXT_DIR / 'outline_issue_report.txt').exists(),
                'step8_report': (PIPELINE_OUTPUT_ROOT / 'generated' / 'report_draft.md').exists(),
                'step9_html': (PIPELINE_OUTPUT_ROOT / 'generated' / 'report.html').exists(),
            }

            return jsonify({'success': True, 'status': status})

        except Exception as e:
            return jsonify({'success': False, 'error': str(e)}), 500

    def run_toc_pipeline(session_id, input_dir, output_dir, doc_type='report', attach_pages=1):
        try:
            pipeline_jobs[session_id] = {'status': 'running', 'step': 2}
            from converters.pipeline import step2_extract, step3_domain, step4_chunk, step5_rag, step6_corpus, step7_index

            #  댁 怨
                         
            input_files = sorted(Path(input_dir).glob('*')) if Path(input_dir).exists() else []
            file_hashes = []
            new_files   = []   # RAG 罹   HIT: {f.name} ({h})", flush=True)
                    else:
                        new_files.append(f)
                        print(f"[DB] RAG 罹 MISS: {f.name} ({h})", flush=True)
                except Exception as de:
                    print(f"[DB] 罹 議고 ㅽ, 洹 泥: {de}", flush=True)
                    new_files.append(f)

            #  洹  step2~5 ㅽ 
            if new_files:
                step2_extract.process_all_pdfs(input_dir, output_dir)
                pipeline_jobs[session_id]['step'] = 3
                step3_domain.main(input_dir, output_dir)
                pipeline_jobs[session_id]['step'] = 4
                step4_chunk.main(output_dir, output_dir)
                pipeline_jobs[session_id]['step'] = 5
                step5_rag.main(output_dir, output_dir)

                #  RAG 寃곌낵臾 DB  
                faiss_path   = rag_dir / 'faiss.index'
                vectors_path = rag_dir / 'vectors.npy'
                meta_path    = rag_dir / 'meta.json'
                chunks_files = list(rag_dir.glob('*_chunks.json'))

                faiss_bytes   = faiss_path.read_bytes()   if faiss_path.exists()   else b''
                vectors_bytes = vectors_path.read_bytes() if vectors_path.exists() else b''
                meta_text     = meta_path.read_text(encoding='utf-8') if meta_path.exists() else ''
                chunks_text   = chunks_files[0].read_text(encoding='utf-8') if chunks_files else ''

                for f in new_files:
                    h = hashlib.md5(f.read_bytes()).hexdigest()
                    try:
                        with get_conn() as conn:
                            with conn.cursor() as cur:
                                cur.execute("""
                                    INSERT INTO files (file_hash, filename)
                                    VALUES (%s, %s)
                                    ON CONFLICT (file_hash) DO NOTHING
                                """, (h, f.name))
                                cur.execute("""
                                    INSERT INTO rag_cache (file_hash, chunks_json, faiss_index, vectors, meta_json)
                                    VALUES (%s, %s, %s, %s, %s)
                                    ON CONFLICT (file_hash) DO NOTHING
                                """, (h, chunks_text,
                                      psycopg2.Binary(faiss_bytes),
                                      psycopg2.Binary(vectors_bytes),
                                      meta_text))
                            conn.commit()
                        print(f"[DB] RAG 罹 : {f.name}", flush=True)
                    except Exception as de:
                        print(f"[DB] RAG  ㅽ: {de}", flush=True)
            else:
                print("[DB] 紐⑤  HIT  step2/4/5 ㅽ, step3 ㅽ + 罹 蹂듭
                                                                              RAG 寃곌낵臾 蹂듭 蹂듭
                                                                                                     猷", flush=True)
                except Exception as de:
                    print(f"[DB] 罹 蹂듭ㅽ, step4~5 ㅽ: {de}", flush=True)
                    step4_chunk.main(output_dir, output_dir)
                    step5_rag.main(output_dir, output_dir)
                pipeline_jobs[session_id]['step'] = 5

            #  step6~7  ㅽ 
            pipeline_jobs[session_id]['step'] = 6
            step6_corpus.main(output_dir, output_dir)
            pipeline_jobs[session_id]['step'] = 7
            step7_index.main(output_dir, output_dir, doc_type=doc_type)

            outline_txt = Path(output_dir) / 'context' / 'outline_issue_report.txt'
            print("[DEBUG outline]", outline_txt.read_text(encoding='utf-8')[:500], flush=True)

            #  sessions / outlines DB  
            outline_text = outline_txt.read_text(encoding='utf-8') if outline_txt.exists() else ''
            try:
                with get_conn() as conn:
                    with conn.cursor() as cur:
                        cur.execute("""
                            INSERT INTO sessions (session_id, file_hashes, doc_type)
                            VALUES (%s, %s, %s)
                            ON CONFLICT (session_id) DO UPDATE SET doc_type=EXCLUDED.doc_type
                        """, (session_id, file_hashes, doc_type))
                        cur.execute("""
                            INSERT INTO outlines (session_id, outline_text)
                            VALUES (%s, %s)
                            ON CONFLICT (session_id) DO UPDATE SET outline_text=EXCLUDED.outline_text
                        """, (session_id, outline_text))
                    conn.commit()
                print(f"[DB] session/outline  
                                                 猷: {session_id}", flush=True)
            except Exception as de:
                print(f"[DB] session/outline  ㅽ: {de}", flush=True)

            pipeline_jobs[session_id] = {'status': 'done', 'doc_type': doc_type}

        except Exception as e:
            import traceback
            print(f"[PIPELINE ERROR] {e}", flush=True)
            print(traceback.format_exc(), flush=True)
            pipeline_jobs[session_id] = {'status': 'error', 'error': str(e)}


    # ===== 
              ㅽ API =====
    @app.route('/api/generate-toc', methods=['POST'])
    def generate_toc():
        """
        紐⑹감 
                API (step3  4  5  6  7)

        
          硫
             
                 寃쎌: step3 ㅽ ()
        
          硫
               寃쎌: step3 ㅽ

        泥:
            {
                "folder_path": "D:\\...",
                "domain_selected": true/false,
                "selected_domains": ["civil_general", "survey"]
            }

        :
            {
                "success": true,
                "title": "蹂닿
                                 紐",
                "toc_items": [
                    { "num": "1.1.1", "title": "...", "guide": "...", "keywords": [...] }
                ]
            }
        """
        try:
            data = request.get_json()
            session_id = data.get('session_id', '')
            domain_selected = data.get('domain_selected', False)
            write_mode = data.get('write_mode', 'restructure')
            instruction = data.get('instruction', '')

            if not session_id:
                return jsonify({'success': False, 'error': 'session_id媛 듬. 
                                                                                    癒쇱 
濡
    몄.'})

            input_dir = f'/tmp/{session_id}/input'
            output_dir = f'/tmp/{session_id}/output'
            os.makedirs(output_dir, exist_ok=True)

            doc_type = data.get('doc_type', 'report')
            attach_pages = int(data.get('attach_pages', 1))
            t = threading.Thread(target=run_toc_pipeline, args=(session_id, input_dir, output_dir, doc_type, attach_pages))
            t.daemon = True
            t.start()
            return jsonify({'success': True, 'status': 'processing', 'session_id': session_id})

        except Exception as e:
            return jsonify({'success': False, 'error': str(e)}), 500

    @app.route('/api/toc-status/<session_id>', methods=['GET'])
    def toc_status(session_id):
        job = pipeline_jobs.get(session_id, {'status': 'unknown'})
        if job.get('status') == 'done':
            outline_path = Path(f'/tmp/{session_id}/output/context/outline_issue_report.txt')
            if outline_path.exists():
                doc_type = job.get('doc_type', 'report')
                if doc_type == 'briefing':
                    toc_items = parse_briefing_plan_for_frontend(outline_path)
                else:
                    toc_items = parse_outline_for_frontend(outline_path)
                return jsonify({'status': 'done', 'toc_items': toc_items})
        return jsonify(job)


    @app.route('/api/generate-report-from-toc', methods=['POST'])
    def generate_report_from_toc():
        """
        몄 紐⑹감濡 蹂닿
                              
                                (step8  step9)

        泥:
            {
                "toc_items": [...],      # 몄 紐⑹감
                "write_mode": "restructure",
                "instruction": "..."
            }
        """
        try:
            data = request.get_json()
            session_id = data.get('session_id', '')
            toc_items = data.get('toc_items', [])
            write_mode = data.get('write_mode', 'restructure')
            instruction = data.get('instruction', '')

            if not session_id:
                return jsonify({'success': False, 'error': 'session_id媛 듬.'})

            input_dir = f'/tmp/{session_id}/input'
            output_dir = f'/tmp/{session_id}/output'

            from converters.pipeline import step8_content, step9_html

            doc_type = data.get('doc_type', 'report')

            step8_content.main(output_dir, output_dir, doc_type=doc_type)
            step9_html.main(output_dir, output_dir, doc_type=doc_type)

            report_html_path = Path(output_dir) / 'generated' / 'report.html'

            # briefing_content 쇰㈃ None)
            briefing_json_path = Path(output_dir) / 'generated' / 'briefing_content.json'
            briefing_content = None
            if briefing_json_path.exists():
                briefing_content = json.loads(briefing_json_path.read_text(encoding='utf-8'))

            # 湲곗〈 html 諛吏 吏 + briefing_content 
                                                        留 異媛
            if report_html_path.exists():
                html = report_html_path.read_text(encoding='utf-8')
                #  briefing 寃곌낵臾 DB  
                try:
                    with get_conn() as conn:
                        with conn.cursor() as cur:
                            cur.execute("""
                                INSERT INTO briefings (session_id, briefing_json, html)
                                VALUES (%s, %s, %s)
                                ON CONFLICT (session_id) DO UPDATE
                                SET briefing_json=EXCLUDED.briefing_json, html=EXCLUDED.html
                            """, (session_id,
                                  json.dumps(briefing_content, ensure_ascii=False) if briefing_content else '',
                                  html))
                        conn.commit()
                    print(f"[DB] briefing  
                                              猷: {session_id}", flush=True)
                except Exception as de:
                    print(f"[DB] briefing  ㅽ: {de}", flush=True)
                return jsonify({
                    'success': True,
                    'html': html,
                    'briefing_content': briefing_content
                })
            else:
                return jsonify({
                    'success': False,
                    'error': '蹂닿
                                     
                                      깆ㅽ⑦듬.'
                })

        except Exception as e:
            return jsonify({'success': False, 'error': str(e)}), 500



    @app.route('/api/check-folder', methods=['POST'])
    def check_folder():
        """대 寃쎈
                         遺
                           瑜대瑜 李얠
                                         듬.'})

            SUPPORTED = {'.hwpx', '.hwp', '.pdf', '.docx', '.xlsx', '.pptx', '.txt', '.csv', 'md', 'json','img', 'png', 'html'}

            all_files = [f for f in folder.rglob('*') if f.is_file()]
            ok_files = [f for f in all_files if f.suffix.lower() in SUPPORTED]
            unknown_files = [f for f in all_files if f.suffix.lower() not in SUPPORTED]

            return jsonify({
                'success': True,
                'total': len(all_files),
                'ok': len(ok_files),
                'unknown': len(unknown_files),
                'ok_list': [{'name': f.name, 'size': f.stat().st_size} for f in ok_files],
                'unknown_list': [f.name for f in unknown_files]
            })
        except Exception as e:
            return jsonify({'success': False, 'error': str(e)}), 500

    @app.route('/api/analyze-briefing', methods=['POST'])
    def analyze_briefing():
        """
        
濡 
        遺
          
           댁
              湲고
                    援ъ
                        怨
                            諛
濡 
        
ㅽ 異異
            source_text = content
            if session_id:
                input_dir = Path(f'/tmp/{session_id}/input')
                output_dir = Path(f'/tmp/{session_id}/output')
                if input_dir.exists():
                    try:
                        from converters.pipeline import step2_extract
                        output_dir.mkdir(parents=True, exist_ok=True)
                        step2_extract.process_all_pdfs(str(input_dir), str(output_dir))
                    except Exception as ex:
                        print(f"step2 異異 
                                             
                                               댁⑹듬.'})

            client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

            prompt = f"""ㅼ 臾몄
                                 瑜 遺
                                       
                                         A4 湲고
                                                  濡 뎄
                                                         깊 怨
                                                               
                                                                   JSON쇰 諛
                                                                               댁:
{source_text[:4000]}

諛, 
      ㅻ
  紐",
      "sections": [
        {{"type": "由щ諛", "content": "듭 硫吏  以
                                                         "}},
        {{"type": "
                    뱀

뱀
紐 + 댁
          댁 "}},
        {{"type": "
                    뱀

뱀
紐 + 댁
          댁 "}},
        {{"type": "⑤", "content": "듭 寃곕  以
                                                      "}}
      ]
    }},
    {{
      "page": 2,
      "title": "[泥⑤]  紐",
      "sections": [
        {{"type": "
                    뱀
⑤ 댁 "}},
        {{"type": "⑤", "content": "듭 寃곕"}}
      ]
    }}
  ]
}}

洹移:
- 蹂몃Ц 1 + 
                泥⑤ 1 (댁⑹
                                 留)
- 媛 
      뱀
 ㅼ 臾몄
           댁 湲곕쇰 援ъ껜쇰 
                                      
- JSON留 諛щㅼ 肄釉濡釉濡嫄
            raw = raw.replace('```json', '').replace('```', '').strip()
            plan = json.loads(raw)

            return jsonify({'success': True, 'plan': plan})

        except Exception as e:
            import traceback
            print(traceback.format_exc(), flush=True)
            return jsonify({'success': False, 'error': str(e)}), 500


    @app.route('/api/generate-briefing', methods=['POST'])
    def generate_briefing():
        """
        援ъ
            怨
                + 肄硫 諛 ㅼ A4 Navy HTML 湲고
                                                       
                                                        
        """
        try:
            import openai
            data = request.get_json()
            session_id = data.get('session_id', '')
            plan = data.get('plan', {})
            comment = data.get('comment', '')
            content = data.get('content', '')
            doc_type = data.get('doc_type', '')

            #  
ㅽ 
            source_text = content
            if session_id:
                input_dir = Path(f'/tmp/{session_id}/input')
                output_dir = Path(f'/tmp/{session_id}/output')

                if input_dir.exists():
                    # 
 step2濡 PDF  
ㅽ 蹂쇱 ㅽ
                    try:
                        from converters.pipeline import step2_extract
                        output_dir.mkdir(parents=True, exist_ok=True)
                        step2_extract.process_all_pdfs(str(input_dir), str(output_dir))
                    except Exception as ex:
                        print(f"step2 異異 
 異異 .md 
               ъ⑹硫 (諛 諛):\n{comment}" if comment else ""

            prompt = f"""ㅼ 援ъ
                                 怨
                                    怨  臾몄
                                                 瑜 諛쇰 A4 HTML 湲고
                                                                          瑜 
                                                                               깊
                                                                                  怨
                                                                                     :
{plan_str}

 臾몄
       :
{source_text[:5000]}

[異 洹移]
- 
   
由 援ъ“瑜  곕 寃
- Navy 而щ ㅽ 吏 (#1a365d, #2c5282, #f7fafc)
- .sheet  overflow: hidden  댁⑹쇰  硫  
- 媛  蹂
           
              <div class="sheet">濡 援щ

- HTML 
        泥 肄留 諛
                    ㅻ
 
由]
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700;900&display=swap');
:root {{
  --primary-navy: #1a365d; --secondary-navy: #2c5282;
  --dark-gray: #2d3748; --medium-gray: #4a5568;
  --light-gray: #e2e8f0; --bg-light: #f7fafc;
  --text-black: #1a202c; --border-color: #cbd5e0;
}}
* {{ margin:0; padding:0; box-sizing:border-box; -webkit-print-color-adjust:exact; }}
body {{ font-family:'Noto Sans KR',sans-serif; background:#f0f0f0; display:flex; flex-direction:column; align-items:center; padding:20px 0; gap:20px; word-break:keep-all; }}
.sheet {{ background:white; width:210mm; height:297mm; padding:20mm; box-shadow:0 0 10px rgba(0,0,0,.1); position:relative; display:flex; flex-direction:column; overflow:hidden; }}
@media print {{ body{{background:none;padding:0;gap:0;}} .sheet{{box-shadow:none;page-break-after:always;}} }}
.page-header {{ display:flex; justify-content:space-between; font-size:9pt; color:var(--medium-gray); margin-bottom:20px; }}
.header-title {{ font-size:22pt; font-weight:900; color:var(--primary-navy); letter-spacing:-1px; text-align:center; margin-bottom:8px; }}
.title-divider {{ height:3px; background:linear-gradient(90deg,var(--primary-navy),var(--secondary-navy)); margin-bottom:20px; }}
.lead-box {{ background:var(--bg-light); border-left:4px solid var(--primary-navy); padding:14px 16px; margin-bottom:16px; font-size:11pt; font-weight:500; color:var(--dark-gray); line-height:1.6; }}
.section {{ margin-bottom:14px; }}
.section-title {{ font-size:11.5pt; font-weight:700; color:var(--primary-navy); display:flex; align-items:center; margin-bottom:8px; }}
.section-title::before {{ content:""; display:inline-block; width:8px; height:8px; background:var(--secondary-navy); margin-right:10px; }}
ul {{ list-style:none; padding-left:10px; }}
li {{ font-size:10pt; position:relative; padding-left:14px; margin-bottom:5px; color:var(--dark-gray); line-height:1.55; }}
li::before {{ content:""; position:absolute; left:0; color:var(--secondary-navy); }}
.data-table {{ width:100%; border-collapse:collapse; font-size:9.5pt; border-top:2px solid var(--primary-navy); margin-top:6px; }}
.data-table th {{ background:var(--primary-navy); color:#fff; padding:8px 6px; border:1px solid var(--secondary-navy); text-align:center; font-size:9pt; }}
.data-table td {{ border:1px solid var(--border-color); padding:6px 8px; color:var(--dark-gray); }}
.data-table td:first-child {{ background:var(--bg-light); font-weight:600; text-align:center; }}
.two-col {{ display:flex; gap:12px; margin-top:6px; }}
.info-box {{ flex:1; background:var(--bg-light); border:1px solid var(--border-color); padding:10px 12px; }}
.info-box-title {{ font-weight:700; color:var(--primary-navy); font-size:10pt; margin-bottom:4px; }}
.info-box p {{ font-size:10pt; color:var(--dark-gray); line-height:1.5; }}
.bottom-box {{ border:1.5px solid var(--border-color); display:flex; margin-top:auto; min-height:65px; margin-bottom:8px; }}
.bottom-left {{ width:18%; background:var(--primary-navy); padding:12px; display:flex; align-items:center; justify-content:center; text-align:center; font-weight:700; font-size:10pt; color:#fff; line-height:1.4; }}
.bottom-right {{ width:82%; background:var(--bg-light); padding:12px 18px; font-size:10pt; line-height:1.6; display:flex; flex-direction:column; justify-content:center; color:var(--dark-gray); }}
.page-footer {{ position:absolute; bottom:10mm; left:20mm; right:20mm; padding-top:8px; text-align:center; font-size:8.5pt; color:var(--medium-gray); border-top:1px solid var(--light-gray); }}
b {{ font-weight:700; color:var(--primary-navy); }}
</style>
</head>
<body>
<!-- ш린ㅼ
              댁⑹ 
                     -->
</body>
</html>"""

            resp = client.chat.completions.create(
                model=os.getenv('OPENAI_MODEL', 'gpt-4o'),
                messages=[{'role': 'user', 'content': prompt}],
                temperature=0.4,
                max_tokens=4000
            )

            html = resp.choices[0].message.content.strip()
            # 肄釉濡嫄
            if html.startswith('```'):
                html = html.split('\n', 1)[1]
                html = html.rsplit('```', 1)[0]

            return jsonify({'success': True, 'html': html})

        except Exception as e:
            import traceback
            print(traceback.format_exc(), flush=True)
            return jsonify({'success': False, 'error': str(e)}), 500



def parse_outline_for_frontend(outline_path: Path) -> list:
    """
    outline_issue_report.txt瑜 깊
    displayTocWithAnimation() 쇰 蹂紐⑹감 紐",
            "guide": "吏
                          媛",
            "keywords": ["ㅼ1", "ㅼ2"]
        }
    ]
    """
    import re

    raw = outline_path.read_text(encoding='utf-8', errors='ignore').splitlines()
    if not raw:
        return []

    report_title = raw[0].strip()
    items = []

    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*(.+)$')
    re_keywords = re.compile(r'(#\S+)')

    current_l3 = None

    for ln in raw[1:]:
        line = ln.strip()
        if not line:
            continue

        m3h = re_l3_head.match(line)
        if m3h:
            current_l3 = {
                'num': m3h.group(1),
                'title': m3h.group(2),
                'report_title': report_title,
                'guide': '',
                'keywords': []
            }
            items.append(current_l3)
            continue

        m3t = re_l3_topic.match(line)
        if m3t and current_l3:
            kws = [k.lstrip('#').strip() for k in re_keywords.findall(m3t.group(2))]
            # 湲곗〈 ㅼ媛
            current_l3['keywords'].extend(kws)
            # 媛 
                   
            if current_l3['guide']:
                current_l3['guide'] += ' / '
            current_l3['guide'] += m3t.group(4)

    return items

def parse_briefing_plan_for_frontend(outline_path: Path) -> list:
    raw = outline_path.read_text(encoding='utf-8', errors='ignore').strip()
    raw_lines = raw.splitlines()
    if not raw_lines:
        return []

    # 
        /  媛 ⑤
 以
   濡  寃쎌 ㅼ 以
                    怨 ⑹
    merged = []
    idx = 0
    while idx < len(raw_lines):
        ln = raw_lines[idx].strip()
        if ln in ['
                    ', ''] and idx + 1 < len(raw_lines):
            merged.append(ln + ' ' + raw_lines[idx + 1].strip())
            idx += 2
            continue
        merged.append(raw_lines[idx])
        idx += 1
    lines = merged

    items = []
    current_page = None

    for line in lines:
        line = line.strip()
        if not line:
            continue

        if '
             ' in line or '' in line:
            icon = '
                     ' if '
                            ' in line else ''
            title = line.replace('
                                   ', '').replace('', '').strip()
            # "蹂몃Ц N"  "蹂몃Ц", "泥⑤ N"  "泥⑤ N"
            import re as _re
            title = _re.sub(r'蹂몃Ц\s*\d*?', '蹂몃Ц', title).strip()
            title = _re.sub(r'泥⑤\s*(\d+)?', r'泥⑤ \1', title).strip()
            if '紐:' in title:
                title = title.split('紐:')[-1].strip()
            if not title:
                title = '蹂몃Ц' if icon == '
                                             ' else '泥⑤'
            current_page = {
                'num': icon,
                'title': title,
                'guide': '',
                'keywords': [],
                'sections': []
            }
            items.append(current_page)

        elif current_page is not None and line.strip():
            content = line.lstrip('-').strip()

            if content.startswith(':'):
                continue

            if content.startswith('紐:'):
                current_page['title'] = content.replace('紐:', '').strip()
                continue

            # 由щ諛/⑤   (": " щ諛/⑤ ы⑤ 蹂
                                                             
                                                                泥)
            import re as _re
            if _re.match(r'^由щ諛\s*:', content):
                lead_text = content.split(':', 1)[-1].strip()
                current_page['sections'].append({'label': '由щ諛', 'text': lead_text})
                continue
            if _re.match(r'^⑤\s*:', content):
                bottom_text = content.split(':', 1)[-1].strip()
                current_page['sections'].append({'label': '⑤', 'text': bottom_text})
                continue

            if '|' in content:
                parts = [p.strip() for p in content.split('|')]
                section_name = parts[0].split(':')[-1].strip()
                comment = parts[1] if len(parts) > 1 else ''
                fmt = parts[3] if len(parts) > 3 else (parts[2] if len(parts) > 2 else '')
                current_page['sections'].append({
                    'label': section_name,
                    'text': comment,
                    'fmt': fmt
                })
            else:
                current_page['sections'].append({'label': content, 'text': ''})

    # guide 嫄 - sections 由ъㅽ 洹몃濡 
                                           濡몄
                                                 
    for item in items:
        item['guide'] = ''  # 
                               濡몄
                                      sections 吏 ъ

    return items
