from fastapi import APIRouter, HTTPException
from app.models import curriculum_module
from app.models.ai_curriculum_module import generate_ai_curriculum
from app.schemas.curriculum_schema import StartCurriculumRequest, ChatCurriculumRequest, GetQuizRequest, CheckQuizRequest, CheckFEQuizRequest, GenerateJournalRequest, GetJournalRequest, CertQuizRequest, CheckCertQuizRequest
from app.core.config import get_gemini_model, PPT_PATH, CURRICULUM_PDF_PATH, PROGRESS_PDF_PATH, IMG_PATH, IMG_EXP_PATH, IMG_Q_PATH, APP_PREFIX
import re
from app.services.goalskill_classifier import classify_and_save
from app.models.report_db_module import save_daily_log
from google.genai import types
import json
import os
import logging
from fastapi.responses import FileResponse


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

router = APIRouter(
    prefix="",
    tags=["Curriculum"]
)
file_router = APIRouter(
    prefix="",
    tags=["Files"]
)

SYSTEM_PROMPT = """
あなたは学習者の「親しい友人（Best Friend）」です。
以下のルールを絶対に守ってください：
1. **言語**: 日本語のみ使用。
2. **口調**: 絶対に敬語（〜です、〜ます）を使わないこと。「〜だよ」「〜じゃん」「〜だね」といった、仲の良い友達と話す「タメ口」を使うこと。
3. **態度**: 明るく、ポジティブに、絵文字（🔥, ✨, 💪, 😂など）を多用して会話すること。
4. **役割**: 先生ではなく、一緒に走るパートナーとして振る舞うこと。
"""


#get_user_progress = 유저의 현재 일차 확인
#get_previous_feedback = 유저의 디버그 일지 확인
#get_today_item_info = 오늘(N일차)의 커리큘럼 아이템 정보 조회 (user_curriculum + master_items)

@router.post("/start")
async def start_curriculum(request: StartCurriculumRequest):
    try:
        session_id = request.session_id
        
        # 1. 진도 확인 (기존 로직 유지)
        progress = curriculum_module.get_user_progress(session_id) # 1/2 이렇게 데이터가 들어감
        if not progress:
             return {"status": "error", "message": "進捗データが見つかりません。"}
        
        current_day = progress['current_day']
        status = progress['status']
        
        #  오늘치 커리큘럼이 user_curriculum에 없으면 자동 배정 (1~2개만)
        curriculum_module.init_user_curriculum(session_id, current_day)
        
        # 0(진행중)이 아니면 시작 안 함 (status는 varchar이므로 문자열 비교)
        if str(status) not in ('0', 'NOT_STARTED'):
            return {"status": "completed", "message": "本日の学習は既に完了しています。"}

        # 2. 데이터 조회 (기존 로직 유지)
        prev_data = curriculum_module.get_previous_feedback(session_id, current_day)
        today_item = curriculum_module.get_today_item_info(session_id, current_day)
        
        if not today_item:
             return {"status": "error", "message": "本日のカリキュラムが見つかりません。"}

        # 3. 프롬프트 구성 [수정됨]
        item_name = today_item['name']
        prev_summary = prev_data['summary_content'] if prev_data else "なし"
        prev_feedback = prev_data['ai_feedback'] if prev_data else "なし"
        prev_praise = prev_data['praise_content'] if prev_data else "なし"
        
        # ★ 여기가 핵심 수정 부분입니다 ★
        prompt = f"""
            あなたは学習者（親しい友人）のパートナーです。
            今日は学習 **Day {current_day}** です。
            今日のテーマは「**{item_name}**」です。
            
            [昨日の振り返り]
            - 学習内容: {prev_summary}
            - AIフィードバック: {prev_feedback}
            - 称賛: {prev_praise}
            
            これらを踏まえて、今日の学習を始めるユーザーに挨拶をしてください。
            
            【絶対ルール - 厳守すること】
            1. **分割出力**: 発言を短く切り、2〜3つの吹き出しに分けるため、文の区切りに「|||」を入れてください。
               (例: おっす！久しぶり！|||今日はこのテーマだね。|||準備はいい？)
            2. **短文**: 1つの吹き出しは短く、テンポよく。長文は禁止。
            3. **強調**: 重要な単語（Day数やテーマ名）は **太字** (例: **Day {current_day}**) にして。
            4. **トーン**: 敬語禁止。親友のように明るく、絵文字を使って。
            5. **内容**: 昨日の頑張りを一言褒めて、今日のテーマの面白さを伝えて、最後に「準備はいい？」と聞く。
            """
        
        client = get_gemini_model()
        
        # (기존 코드 유지)
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[types.Content(role="user", parts=[types.Part(text=prompt)])],
            config=types.GenerateContentConfig(
                system_instruction=SYSTEM_PROMPT,
                temperature=0.8 # 창의성 약간 높임
            )
        )
        
        # Goalskill 분류: Part C, AI 시작 인사
        await classify_and_save(session_id=session_id, sender="I", part="C", text=response.text)
        save_daily_log(session_id, "bot", response.text)

        return {
            "status": "start",
            "message": response.text,
            "current_day": current_day,
            "item_id": today_item['item_id']
        }

    except Exception as e:
        logger.error(f"Start Error: {e}") # 로깅 추가
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/chat")
async def chat_curriculum(request: ChatCurriculumRequest):
    try:
        client = get_gemini_model()
        
        # 1. 시작 단계 (긍정/부정 판별)
        if request.current_step == 'START':
            # Goalskill 분류: Part C, User 메시지 (START)
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=request.user_message)
            save_daily_log(request.session_id, "user", request.user_message)

            prompt = f"""
            ユーザーの発言: "{request.user_message}"
            
            この発言が「ポジティブ(やる気がある)」か「ネガティブ(やる気がない・不安)」か判定してください。
            
            もし「ネガティブ」なら、「目標を達成するためには、今やるしかない！」というニュアンスの厳しめの応援メッセージを作成してください。
            もし「ポジティブ」なら、「その意気だ！必ず目標は達成できる！」というニュアンスの強い称賛メッセージを作成してください。
            
            出力フォーマット(JSON):
            {{
                "sentiment": "POSITIVE" or "NEGATIVE",
                "message": "作成したメッセージ"
            }}
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=prompt)])],
                config={"response_mime_type": "application/json"}
            )
            result_data = json.loads(response.text)
            # Goalskill 분류: Part C, AI 응답 (START)
            await classify_and_save(session_id=request.session_id, sender="I", part="C", text=result_data.get("message", ""))
            save_daily_log(request.session_id, "bot", result_data.get("message", ""))
            return result_data

        # 2. 유튜브 완료 확인
        elif request.current_step == 'YOUTUBE':
            # Goalskill 분류: Part C, User 메시지 (YOUTUBE)
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=request.user_message)
            save_daily_log(request.session_id, "user", request.user_message)

            prompt_check = f"""
            ユーザーの発言: "{request.user_message}"
            
            タスク: ユーザーが動画を見終わった、または次に進みたいという意思があるか判定してください。
            前の文脈で「動画見終わったら教えてね」と言っています。

            [判定基準 - **激甘(Very Lenient)**]
            1. **YES (True)**: 
               - 「はい」「うん」「ok」「OK」「みた」「見た」「次」「next」「やった」などの短い肯定。
               - 「終わった」「完了」「理解した」などの完了報告。
               - スタンプや絵文字（👍, 👌）の代わりのような短い言葉。
            2. **NO (False)**: 
               - 「まだ」「ちょっと待って」「質問がある」などの明確な否定や質問のみ。
            
            JSONで {{"is_finished": true}} または {{"is_finished": false}} を返してください。
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=prompt_check)])],
                config={"response_mime_type": "application/json"}
            )
            result = json.loads(response.text)
            
            if result['is_finished']:
                session_id = request.session_id
                
                # =================================================================
                # [수정] item_id가 None일 경우, DB에서 현재 진도를 조회하여 자동 탐지
                # =================================================================
                target_item_id = request.item_id
                
                if target_item_id is None:
                    print("!!! item_id is Missing -> Auto-detecting from Progress !!!")
                    # 1. 현재 몇 일차인지 확인
                    progress = curriculum_module.get_user_progress(session_id)
                    if progress:
                        current_day = progress['current_day']
                        # 2. 해당 날짜의 대표 아이템 ID 하나만 가져오기
                        today_info = curriculum_module.get_today_item_info(session_id, current_day)
                        if today_info:
                            target_item_id = today_info['item_id']
                            print(f"   -> Auto-detected item_id: {target_item_id}")

                # =================================================================
                # 1. 데이터 조회 (탐지된 target_item_id 사용)
                # =================================================================
                if target_item_id:
                    daily_items, _ = curriculum_module.get_daily_items_and_resources(session_id, target_item_id)
                else:
                    daily_items = [] # 여전히 못 찾았으면 빈 리스트

                print(f"============== [DEBUG LOG] ==============")
                print(f"1. Target item_id: {target_item_id}")
                print(f"2. Daily Items Found: {len(daily_items) if daily_items else 0}")

                # [수정] PPT를 learning_resources DB에서 조회 (파일시스템 직접 검색 → DB 조회)
                valid_ppts = []
                valid_item_names = []

                if daily_items:
                    item_ids = [item['item_id'] for item in daily_items]
                    ppt_resources = curriculum_module.get_ppt_resources(item_ids)
                    
                    for ppt in ppt_resources:
                        file_name = ppt['url']  # learning_resources.url에 파일명 저장됨
                        viewer_url = f"components/ppt_viewer.html?file={file_name}"
                        
                        print(f"3. PPT from DB: {file_name} -> {viewer_url}")
                        
                        valid_ppts.append({
                            "item_id": ppt['item_id'],
                            "name": ppt['title'],
                            "url": viewer_url 
                        })
                        valid_item_names.append(ppt['title'])
                
                print(f"=========================================")

                # =================================================================
                # 2. AI 프롬프트 구성
                # =================================================================
                if not valid_ppts:
                    # PPT가 하나도 없으면 바로 퀴즈로 넘어가거나 안내
                     return {"is_finished": True, "message": "今回はPPT資料はないみたい！次はクイズに進もうか！", "ppts": []}

                ppt_topic_str = "」と「".join(valid_item_names)
                
                ppt_prompt = f"""
                状況: 親しい友人（Best Friend）です。
                タスク: 学習内容「{ppt_topic_str}」のPPT資料（計{len(valid_ppts)}つ）を渡します。
                
                [制約]
                1. **超短文**: 「これまとめたPPT作ったから見てみて！」くらい短く。
                2. URL禁止。
                """
                ppt_msg_response = client.models.generate_content(
                    model="gemini-2.5-flash",
                    contents=[types.Content(role="user", parts=[types.Part(text=ppt_prompt)])]
                )
                
                return {
                    "is_finished": True, 
                    "message": ppt_msg_response.text,
                    "ppts": valid_ppts 
                }
            else:
                return {"is_finished": False, "message": "OK、見終わったら教えてね！"}
        elif request.current_step == 'PPT':
            # Goalskill 분류: Part C, User 메시지 (PPT)
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=request.user_message)
            save_daily_log(request.session_id, "user", request.user_message)

            prompt_check = f"""
            ユーザーの発言: "{request.user_message}"
            タスク: ユーザーがPPTを見終わったか判定。

            [判定基準 - **激甘**]
            1. **YES**: 「はい」「うん」「OK」「次」「わかった」「見た」はすべて {{ "is_finished": true }}。
            2. **NO**: 「まだ」「質問」などは {{ "is_finished": false }}。
            
            JSONで返してください。
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=prompt_check)])],
                config={"response_mime_type": "application/json"}
            )
            result = json.loads(response.text)
            
            if result['is_finished']:
                quiz_prompt = f"""
                あなたはユーザーの親しい友人（Best Friend）です。
                ユーザーがPPT学習を終えました。次は「実践クイズ（IT問題）」です。

                [メッセージ作成ルール - 厳守]
                1. **超短文**: ダラダラ話さない。「インプット完了！次はアウトプットだ！」くらいのスピード感で。
                2. **分割**: 文章を2〜3つの短い吹き出しに分け、間に「|||」を入れる。
                3. **強調**: 「**クイズ**」や「**実践**」などの単語は太字にする。
                4. **内容**: 失敗を恐れずに挑戦しよう、というポジティブなメッセージを短く。
                
                例:
                PPTお疲れ！インプットはバッチリだね！|||
                じゃあ次は、**実践クイズ**で腕試ししてみようか！|||
                間違えても大丈夫！**アウトプット**が大事だからね！
                """
                quiz_msg_response = client.models.generate_content(
                    model="gemini-2.5-flash",
                    contents=[types.Content(role="user", parts=[types.Part(text=quiz_prompt)])]
                )
                return {"is_finished": True, "message": quiz_msg_response.text}
            else:
                return {"is_finished": False, "message": "了解！見終わったら教えて！"}

        # =========================================================
        # THEORY 단계: 이론 학습 완료 확인 (PPT와 동일 로직)
        # =========================================================
        elif request.current_step == 'THEORY':
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=request.user_message)
            save_daily_log(request.session_id, "user", request.user_message)

            prompt_check = f"""
            ユーザーの発言: "{request.user_message}"
            タスク: ユーザーが理論学習を見終わったか判定。

            [判定基準 - **激甘**]
            1. **YES**: 「はい」「うん」「OK」「次」「わかった」「見た」はすべて {{ "is_finished": true }}。
            2. **NO**: 「まだ」「質問」などは {{ "is_finished": false }}。
            
            JSONで返してください。
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=prompt_check)])],
                config={"response_mime_type": "application/json"}
            )
            result = json.loads(response.text)
            
            if result['is_finished']:
                fe_intro_prompt = f"""
                あなたはユーザーの親しい友人（Best Friend）です。
                ユーザーが資格試験の理論学習を終えました。次は「基本情報技術者試験の練習問題」です。

                [メッセージ作成ルール - 厳守]
                1. **超短文**: ダラダラ話さない。「理論バッチリ！次は問題だ！」くらいのスピード感で。
                2. **分割**: 文章を2〜3つの短い吹き出しに分け、間に「|||」を入れる。
                3. **強調**: 「**基本情報**」や「**実力試し**」などの単語は太字にする。
                4. **内容**: 理論学習を褒めて、次のFE問題へ誘導する。
                
                例:
                理論学習お疲れ！頭に入ったかな？|||
                じゃあ次は、**基本情報技術者試験**の問題に挑戦してみようか！|||
                学んだ知識が通用するか、**実力試し**だ！
                """
                fe_msg_response = client.models.generate_content(
                    model="gemini-2.5-flash",
                    contents=[types.Content(role="user", parts=[types.Part(text=fe_intro_prompt)])]
                )
                return {"is_finished": True, "message": fe_msg_response.text}
            else:
                return {"is_finished": False, "message": "了解！見終わったら教えて！"}

        else:
            # 퀴즈 중이거나 알 수 없는 단계에서 사용자가 일반 질문을 한 경우 (일반 채팅 폴백)
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=request.user_message)
            save_daily_log(request.session_id, "user", request.user_message)

            prompt_fallback = f"""
            ユーザーの発言: "{request.user_message}"
            あなたはプログラミングやITの学習をサポートする親しい友人（Best Friend）です。
            ユーザーが学習中（クイズや理論の最中）に質問やコメントをしました。
            
            [タスク]
            1. 質問であれば、わかりやすく簡潔に答えてください。
            2. ただの呟きであれば、共感して応援してください。
            3. タメ口で、親身になって明るく答えること。
            
            必ず以下のJSON形式で返してください:
            {{
                "is_finished": false,
                "message": "作成したメッセージ"
            }}
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=prompt_fallback)])],
                config={"response_mime_type": "application/json"}
            )
            result = json.loads(response.text)
            await classify_and_save(session_id=request.session_id, sender="I", part="C", text=result.get("message", ""))
            save_daily_log(request.session_id, "bot", result.get("message", ""))
            return result

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/resources")
async def get_resources(request: GetQuizRequest):
    try:
        session_id = request.session_id
        
        daily_items, resources = curriculum_module.get_daily_items_and_resources(session_id, request.item_id)
        
        if not daily_items:
             raise HTTPException(status_code=404, detail="Schedule not found")

        topic_names = "」と「".join([item['name'] for item in daily_items])
        
        logger.info(f"Today's Topics: {topic_names}")
        logger.info(f"Total Resources Found: {len(resources)}")

        client = get_gemini_model()
        
        # [수정] 프롬프트: 인사 금지 + 분할 출력( ||| ) + 강조( ** )
        intro_prompt = f"""
        状況: 親しい友人（Best Friend）です。直前の挨拶は済み。
        今日のテーマ「{topic_names}」の動画（{len(resources)}本）を紹介してください。

        [制約事項 - 厳守]
        1. **挨拶禁止**: 「おはよう」などは不可。いきなり本題へ。
        2. **超短文・簡潔**: 
           - ダラダラ説明しない。**1つの吹き出しにつき1〜2文**で短く切る。
           - 「これめっちゃ分かりやすいよ！」「〇〇の仕組みがバッチリ分かるはず！」程度でOK。
        3. **分割出力**: 「|||」で区切って2〜3つに分ける。
        4. **強調**: キーワード（「条件分岐」など）は **太字**。
        5. **URL禁止**。
        """

        intro_msg_response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[types.Content(role="user", parts=[types.Part(text=intro_prompt)])]
        )
        
        return {
            "message": intro_msg_response.text,
            "resources": resources
        }

    except Exception as e:
        logger.error(str(e))
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/quiz")
async def get_quiz(request: GetQuizRequest):
    try:
        # 1. 먼저 요청받은 item_id (예: 17번)로 문제를 찾아봅니다.
        problem = curriculum_module.get_quiz_problem(request.item_id, request.q_num)
        
        # 2. 만약 없다면? 같은 날짜(Day)의 다른 아이템들도 뒤져봅니다. (예: 18번)
        if not problem:
             # 오늘 배울 아이템들을 싹 가져옵니다 (17번, 18번...)
             daily_items, _ = curriculum_module.get_daily_items_and_resources(request.session_id, request.item_id)
             
             if daily_items:
                 for item in daily_items:
                     # 이미 확인한 17번은 건너뛰고
                     if item['item_id'] == request.item_id:
                         continue
                     
                     # 다른 아이템(18번)으로 문제를 다시 조회해봅니다.
                     problem = curriculum_module.get_quiz_problem(item['item_id'], request.q_num)
                     if problem:
                         # 찾았다! (반복 종료)
                         break
        
        # 그래도 없으면 진짜 없는 것
        if not problem:
             raise HTTPException(status_code=404, detail="問題が見つかりません。")
             
        return problem

    except HTTPException as he:
        # 404 같은 HTTP 에러는 그대로 내보냅니다 (500으로 감싸지 않음)
        raise he
    except Exception as e:
        # 진짜 서버 에러만 500으로 처리
        logger.error(f"Quiz Error: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/quiz/check")
async def check_quiz(request: CheckQuizRequest):
    try:
        client = get_gemini_model()
        
        # [수정] 프롬프트 강화: 정답과 해설을 기준점(Ground Truth)으로 제공
        prompt = f"""
        あなたはプログラミング学習の採点官であり、ユーザーの親しい友人です。
        
        [問題]
        {request.question_text}
        
        [正解データ]
        {request.correct_answer}
        
        [解説データ]
        {request.explanation}
        
        [ユーザーの回答]
        {request.user_answer}
        
        [タスク]
        ユーザーの回答が「正解データ」と一致しているか、あるいは意味的に正しいかを判定してください。
        解説データの内容を参考にして、なぜそうなるのかを補足してください。
        
        [出力ルール]
        1. 正解の場合: 「正解！さすがだね！」と褒めて、簡単に解説。
        2. 不正解の場合: 「惜しい！不正解だよ」と励まし、[解説データ]を元に正しい答えを教えてあげる。
        3. C#やJavaなどの別言語の話は絶対にしないこと。今はPythonの学習中。
        4. 口調はタメ口で。
        5. **箇条書き禁止**: 「・」「-」「*」などのリスト記号は絶対に使わないこと。すべて普通の文章（段落）で書くこと。
        """
        
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[types.Content(role="user", parts=[types.Part(text=prompt)])]
        )
        
        # Goalskill 분류: session_id가 있으면 실행
        if request.session_id:
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=request.user_answer)
            save_daily_log(request.session_id, "user", request.user_answer)
            await classify_and_save(session_id=request.session_id, sender="I", part="C", text=response.text)
            save_daily_log(request.session_id, "bot", response.text)
        
        return {"result_message": response.text}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
    

@file_router.get("/ppt/{file_name}")
async def get_ppt_file(file_name: str):
    """
    프론트엔드 URL: /goal-skill-t/api/file/ppt/item_18.pptx
    실제 서버 경로: /home/air/goalskill_t/back/PPT/item_18.pptx
    """
    # 1. 실제 파일이 있는 서버 내부 경로
    base_dir = PPT_PATH
    file_path = os.path.join(base_dir, file_name)
    
    # 2. 파일이 진짜 있는지 확인하고 전송
    if os.path.exists(file_path):
        return FileResponse(
            path=file_path, 
            filename=file_name, 
            # 브라우저가 PPT임을 알 수 있게 미디어 타입 지정
            media_type='application/vnd.openxmlformats-officedocument.presentationml.presentation'
        )
    else:
        # 파일이 없으면 404 에러
        raise HTTPException(status_code=404, detail="File not found on server")

@router.get("/ppt/slides/{file_name}")
async def get_ppt_slides(file_name: str):
    """PPTXファイルのスライド内容をJSON形式で返すAPI"""
    from pptx import Presentation
    from pptx.util import Inches, Pt, Emu
    from pptx.dml.color import RGBColor
    
    file_path = os.path.join(PPT_PATH, file_name)
    if not os.path.exists(file_path):
        raise HTTPException(status_code=404, detail="PPT file not found")
    
    try:
        prs = Presentation(file_path)
        slides_data = []
        
        # スライドサイズ取得
        slide_width = prs.slide_width
        slide_height = prs.slide_height
        
        for slide_idx, slide in enumerate(prs.slides):
            slide_info = {"slide_num": slide_idx + 1, "elements": []}
            
            for shape in slide.shapes:
                element = {"type": "unknown"}
                
                if shape.has_text_frame:
                    paragraphs = []
                    for para in shape.text_frame.paragraphs:
                        runs = []
                        for run in para.runs:
                            run_data = {"text": run.text}
                            if run.font.bold:
                                run_data["bold"] = True
                            if run.font.size:
                                run_data["fontSize"] = run.font.size.pt
                            if run.font.color and run.font.color.rgb:
                                run_data["color"] = str(run.font.color.rgb)
                            runs.append(run_data)
                        
                        if runs or para.text.strip():
                            para_data = {"runs": runs, "text": para.text}
                            if para.alignment is not None:
                                para_data["align"] = str(para.alignment)
                            paragraphs.append(para_data)
                    
                    element = {
                        "type": "text",
                        "paragraphs": paragraphs,
                        "left": shape.left / slide_width * 100 if shape.left else 0,
                        "top": shape.top / slide_height * 100 if shape.top else 0,
                        "width": shape.width / slide_width * 100 if shape.width else 100,
                        "height": shape.height / slide_height * 100 if shape.height else 10,
                    }
                
                elif shape.has_table:
                    table_data = []
                    for row in shape.table.rows:
                        cells = [cell.text for cell in row.cells]
                        table_data.append(cells)
                    element = {"type": "table", "rows": table_data}
                
                if element["type"] != "unknown":
                    slide_info["elements"].append(element)
            
            slides_data.append(slide_info)
        
        return {
            "file": file_name,
            "total_slides": len(slides_data),
            "slides": slides_data
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"PPT parse error: {str(e)}")

# =========================================================
# 자격증 이론 API
# =========================================================
@router.post("/theory/data")
async def get_theory(request: StartCurriculumRequest):
    """현재 일차의 CERT 아이템에 맞는 이론/문제 데이터를 조회"""
    try:
        session_id = request.session_id
        
        # 1. 유저의 현재 일차 조회
        progress = curriculum_module.get_user_progress(session_id)
        if not progress:
            raise HTTPException(status_code=404, detail="Progress not found")
        
        current_day = progress['current_day']
        
        # 2. 해당 일차의 CERT 아이템 조회 (THEORY/PRACTICE 모두)
        cert_item = curriculum_module.get_cert_item_for_day(session_id, current_day)
        
        if not cert_item:
            # CERT 아이템 없음 → 13~20일차(sort_order 13 이상)인 경우에만 복습 모드 발동
            sort_order = ((current_day - 1) % 20) + 1
            if sort_order >= 13:
                chapters = curriculum_module.get_cert_chapters_for_month(session_id, current_day)
                if chapters:
                    return {"found": False, "review_mode": True, "chapters": chapters}
            return {"found": False, "message": "今日は資格試験の学習はないよ！"}
        
        # 3. CERT 아이템 이름에서 챕터 번호 추출 ("第1章 ..." → 1)
        match = re.search(r'第(\d+)章', cert_item['name'])
        if not match:
            return {"found": False, "message": "チャプター情報が見つかりません。"}
        
        section_num = int(match.group(1))
        item_type = cert_item['item_type']  # 'THEORY' or 'PRACTICE'
        
        # 4. PRACTICE일 → 이론 없이 퀴즈만
        if item_type == 'PRACTICE':
            return {
                "found": True,
                "practice_only": True,
                "cert_name": cert_item['name'],
                "section": section_num
            }
        
        # 5. THEORY일 → Theory_DB에서 이론 데이터 조회
        #    item_id가 THEORY_SUB_RANGES에 있으면 서브챕터 범위 필터링 적용
        sub_range = curriculum_module.THEORY_SUB_RANGES.get(cert_item['item_id'])
        theory_items = curriculum_module.get_theory_data(section_num, sub_range=sub_range)
        
        if not theory_items:
            return {"found": False, "message": "理論データが見つかりません。"}
        
        # 6. 이미지 경로를 API URL로 변환
        for item in theory_items:
            if item.get('img') and item['img']:
                img_filename = os.path.basename(item['img'])
                item['img'] = f"/{APP_PREFIX}/api/file/theory-img/section{section_num}/{img_filename}"
        
        # 7. 챕터별로 그룹핑
        chapters = {}
        for item in theory_items:
            ch = item['chapter']
            if ch not in chapters:
                chapters[ch] = {
                    'chapter': ch,
                    'main_title': item['main_title'],
                    'items': []
                }
            chapters[ch]['items'].append(item)
        
        return {
            "found": True,
            "practice_only": False,
            "cert_name": cert_item['name'],
            "section": section_num,
            "chapters": list(chapters.values()),
            "total_items": len(theory_items)
        }
        
    except HTTPException as he:
        raise he
    except Exception as e:
        logger.error(f"Theory Data Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/cert/review/chapters")
async def get_review_chapters(request: StartCurriculumRequest):
    """13~20일차 복습 모드: 해당 월에 배운 장 목록 반환"""
    try:
        session_id = request.session_id
        progress = curriculum_module.get_user_progress(session_id)
        if not progress:
            raise HTTPException(status_code=404, detail="Progress not found")
        
        chapters = curriculum_module.get_cert_chapters_for_month(session_id, progress['current_day'])
        return {"chapters": chapters}
    except Exception as e:
        logger.error(f"Review Chapters Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))

from pydantic import BaseModel
class TheoryIntroRequest(BaseModel):
    cert_name: str

@router.post("/theory/intro")
async def get_theory_intro(request: TheoryIntroRequest):
    """자격증 이론 소개 메시지를 AI로 생성"""
    try:
        client = get_gemini_model()
        prompt = f"""
        あなたはユーザーの親しい友人（Best Friend）です。
        ユーザーが実践クイズを終え、次は資格試験の理論学習「{request.cert_name}」に入ります。

        [メッセージ作成ルール - 厳守]
        1. **超短文**: 2〜3つの短い吹き出しに分け、間に「|||」を入れる。
        2. **強調**: 「**資格試験**」「**理論学習**」「**{request.cert_name}**」などの重要ワードは**太字**にする。
        3. **口調**: 敬語禁止。タメ口で明るく。絵文字1〜2個使用。
        4. **内容**: クイズお疲れ → 次は理論学習だよ → チャプター名を紹介。
        5. **絶対に4つ以上の吹き出しにしない**。3つまで。

        例:
        クイズお疲れ！💪|||次は、**資格試験の理論学習**だよ！|||「**{request.cert_name}**」の内容を確認しよう！
        """
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[types.Content(role="user", parts=[types.Part(text=prompt)])]
        )
        return {"message": response.text}
    except Exception as e:
        logger.error(f"Theory Intro Error: {e}")
        return {"message": f"クイズお疲れ！💪✨|||次は、**資格試験の理論学習**だよ！|||「**{request.cert_name}**」の内容を確認しよう！"}

@router.get("/theory/section/{section_num}")
async def get_theory_section(section_num: int):
    """섹션 번호로 직접 이론 데이터 조회 (복습 모드용)"""
    try:
        theory_items = curriculum_module.get_theory_data(section_num)
        if not theory_items:
            raise HTTPException(status_code=404, detail="Theory data not found")
        
        # 이미지 경로 변환
        for item in theory_items:
            if item.get('img') and item['img']:
                img_filename = os.path.basename(item['img'])
                item['img'] = f"/{APP_PREFIX}/api/file/theory-img/section{section_num}/{img_filename}"
        
        # 챕터별 그룹핑
        chapters = {}
        for item in theory_items:
            ch = item['chapter']
            if ch not in chapters:
                chapters[ch] = {'chapter': ch, 'main_title': item['main_title'], 'items': []}
            chapters[ch]['items'].append(item)
        
        return {
            "found": True,
            "section": section_num,
            "chapters": list(chapters.values()),
            "total_items": len(theory_items)
        }
    except HTTPException as he:
        raise he
    except Exception as e:
        logger.error(f"Theory Section Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@file_router.get("/theory-img/{section}/{filename}")
async def get_theory_image(section: str, filename: str):
    """이론 이미지 파일 서빙"""
    file_path = os.path.join(IMG_PATH, section, filename)
    
    if os.path.exists(file_path):
        return FileResponse(path=file_path, media_type='image/png')
    else:
        raise HTTPException(status_code=404, detail="Image not found")
    
@router.post("/fe/intro")
async def get_fe_intro():
    try:
        client = get_gemini_model()
        # [수정] FE 소개 메시지
        # 요청: "간결하고 짧게, 진하게 강조, 1~3개 말풍선( ||| )"
        prompt = """
        あなたはユーザーの親しい友人です。
        ユーザーがPythonの課題をクリアしました。次は「基本情報技術者試験」の練習問題です。
        
        以下のルールでメッセージを作成してください：
        1. **分割**: 文章を2〜3つの短いフレーズに分け、間に「|||」を入れる。
        2. **短文**: 1つの吹き出しは短く簡潔に。「次はこれだ！」というスピード感で。
        3. **強調**: 「**基本情報**」や「**実力試し**」などは太字にする。
        4. **内容**: Pythonクリアを褒めて、次のFE問題へ誘導する。
        
        例:
        お疲れ！Python完走だね！さすが！|||
        じゃあ次は、**基本情報技術者試験**の問題に挑戦してみようか！|||
        学んだ知識が通用するか、**実力試し**だ！
        """
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[types.Content(role="user", parts=[types.Part(text=prompt)])]
        )
        return {"message": response.text}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# 2. FE 문제 조회
@router.post("/fe/quiz")
async def get_fe_quiz(request: GetQuizRequest): # item_id 등은 무시하고 q_num만 사용
    try:
        # DB 모듈에서 총 개수와 문제 상세 정보 조회
        total_count = curriculum_module.get_fe_problem_count()
        problem = curriculum_module.get_fe_problem_detail(request.q_num)
        
        if not problem:
             raise HTTPException(status_code=404, detail="問題が見つかりません。")
        
        # 마지막 문제인지 확인 (q_num이 전체 개수보다 크거나 같으면 마지막)
        problem['is_last'] = (request.q_num >= total_count)
        
        return problem
    except Exception as e:
        logger.error(str(e))
        raise HTTPException(status_code=500, detail=str(e))

# 3. FE 정답 확인
@router.post("/fe/quiz/check")
async def check_fe_quiz(request: CheckFEQuizRequest):
    try:
        # DB 모듈을 통해 정답 비교 및 해설 가져오기
        result = curriculum_module.check_fe_answer_logic(request.question_number, request.user_answer)
        return result
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# ===== cert_questions 기반 FE 퀴즈 (챕터별 동적) =====
@router.post("/cert/quiz")
async def get_cert_quiz(request: CertQuizRequest):
    """챕터별 cert_questions 문제 조회"""
    try:
        total_count = curriculum_module.get_cert_question_count(request.chapter)
        problem = curriculum_module.get_cert_question_detail(request.chapter, request.q_num)
        
        if not problem:
            raise HTTPException(status_code=404, detail="問題が見つかりません。")
        
        problem['is_last'] = (request.q_num >= total_count)
        problem['total_count'] = total_count
        
        # question_img 경로 변환
        if problem.get('question_img') and problem['question_img']:
            filename = problem['question_img'].split('/')[-1]
            section_dir = f"section{request.chapter}"
            problem['question_img'] = f"/{APP_PREFIX}/api/file/cert-img/{section_dir}/{filename}"
        
        return problem
    except HTTPException as he:
        raise he
    except Exception as e:
        logger.error(f"cert quiz error: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/cert/quiz/check")
async def check_cert_quiz(request: CheckCertQuizRequest):
    """cert_questions 정답 확인 + 해설"""
    try:
        result = curriculum_module.check_cert_answer(request.chapter, request.question_number, request.user_answer)
        
        # solution_img 경로 변환
        if result.get('explanation_type') == 'img' and result.get('explanation'):
            filename = result['explanation'].split('/')[-1]
            section_dir = f"section{request.chapter}"
            result['explanation'] = f"/{APP_PREFIX}/api/file/cert-img/{section_dir}/{filename}"
        
        return result
    except Exception as e:
        logger.error(f"cert quiz check error: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@file_router.get("/cert-img/{section}/{filename}")
async def get_cert_img(section: str, filename: str):
    """cert_questions 관련 이미지 서빙 (question_img, solution_img)"""
    from fastapi.responses import FileResponse
    import os
    img_path = os.path.join(IMG_EXP_PATH, section, filename)
    if not os.path.exists(img_path):
        # img_q 폴더도 확인
        img_path = os.path.join(IMG_Q_PATH, section, filename)
    if not os.path.exists(img_path):
        raise HTTPException(status_code=404, detail="Image not found")
    return FileResponse(img_path)

@router.post("/fe/complete")
async def complete_fe_phase():
    try:
        client = get_gemini_model()
        prompt = """
        ユーザーが基本情報技術者試験（FE）の難しい問題をすべてクリアしました。
        親しい友人として、最高の称賛を送ってください。

        [制約事項 - 厳守]
        1. **超短文**: 長い文章は禁止。テンポよく短く。「マジですごい！」「天才かよ！」など。
        2. **分割**: メッセージを2〜3つの短い吹き出しに分けるため、間に「|||」を入れる。
        3. **強調**: 「**完走**」「**すごい**」「**デバッグ日誌**」などの重要単語は太字にする。
        4. **内容**: 
           - まず完走したことを手放しで褒める。
           - 最後に「今日の学びを忘れないように、**デバッグ日誌**を書こう！」と提案する。
        """
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[types.Content(role="user", parts=[types.Part(text=prompt)])]
        )
        return {"message": response.text}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# 2. 디버그 일지 인터뷰 채팅
# @router.post("/journal/chat")
# async def chat_journal(request: ChatCurriculumRequest):
#     try:
#         client = get_gemini_model()
        
#         # 1. 대화 내역 문자열 변환
#         history_context = ""
#         if request.history:
#             for entry in request.history:
#                 role = "User" if entry['role'] == "user" else "AI"
#                 history_context += f"{role}: {entry['text']}\n"

#         # 2. 시스템 프롬프트 (집요함 방지 룰 추가)
#         system_instruction = """
#         あなたはユーザーの学習を振り返る「デバッグ日誌」の作成を手伝うパートナーです。
#         以下の4つのフェーズ（Phase）に沿って、親しい友人のように質問をしてください。
        
#         [進行フェーズ]
#         Phase 1. 状況把握: 今日の勉強の感想や進捗を聞く。
#         Phase 2. デバッグ: エラー内容や詰まった箇所を聞く。
#         Phase 3. 解決策の参照: 参考にした資料（動画・PPT）を聞く。
#         Phase 4. 解決: 最終的な解決方法と、今の気持ちを聞く。

#         [重要: 会話のルール]
#         1. **深掘り禁止**: ユーザーが「忘れた」「ただ難しかった」「わからない」と曖昧な回答をした場合、**無理に聞き出そうとせず、すぐに「そっか、そういうこともあるよね！」と受け入れて次のフェーズに進んでください。**
#         2. **繰り返し禁止**: ユーザーが同じような回答を2回繰り返したら、これ以上情報は出ないと判断し、次のフェーズに進んでください。
#         3. **内部用語の隠蔽**: プロンプトにある「（壁）」や「Phase N」といった単語は絶対に出力しないでください。
#         4. **完遂優先**: 完璧な情報を集めることよりも、気持ちよく会話を終わらせることを優先してください。

#         [終了判定]
#         全てのフェーズが終わった、またはこれ以上聞くのが野暮だと判断したら:
#         JSONで {"is_finished": true, "message": "OK! いろいろ教えてくれてありがとう！バッチリ整理できそうだよ！"} を返してください。
        
#         まだ途中なら:
#         JSONで {"is_finished": false, "message": "次の質問..."} を返してください。
#         """
        
#         prompt = f"""
#         [これまでの会話履歴]
#         {history_context}
        
#         [ユーザーの最新の発言]
#         "{request.user_message}"
        
#         履歴を分析し、ユーザーが答えに窮している場合や、曖昧な回答の場合は、深掘りせずに次のフェーズの質問に移ってください。
#         """
        
#         response = client.models.generate_content(
#             model="gemini-2.5-flash",
#             contents=[types.Content(role="user", parts=[types.Part(text=prompt)])],
#             config={
#                 "system_instruction": system_instruction,
#                 "response_mime_type": "application/json"
#             }
#         )
#         return json.loads(response.text)
#     except Exception as e:
#         raise HTTPException(status_code=500, detail=str(e))

@router.post("/journal/chat")
async def chat_journal(request: ChatCurriculumRequest):
    try:
        client = get_gemini_model()
        history = request.history or []
        user_input = request.user_message
        
        # 1. 일지 시작 (첫 질문)
        if user_input == "日誌作成を始めよう":
            start_prompt = """
            あなたはユーザーの親しい友人（Best Friend）です。
            今からユーザーと一緒に「学習日誌（デバッグ日誌）」を作成します。
            
            [タスク]
            最初の質問をしてください。
            「よし、日誌書こうか！今日の勉強、どんな感じだった？印象に残ってることとか、進捗はどうかな？」
            というニュアンスで、明るく聞いてください。
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=start_prompt)])]
            )
            # Goalskill 분류: Part C, AI 일지 시작 질문
            await classify_and_save(session_id=request.session_id, sender="I", part="C", text=response.text)
            save_daily_log(request.session_id, "bot", response.text)
            return {"is_finished": False, "message": response.text}

        # 대화 턴 수에 따른 시나리오 진행 (0:시작, 1:AI질문1, 2:User답1, 3:AI질문2...)
        turn_count = len(history)

        # Step 1: 소감 듣고 -> 어려웠던 점 물어보기
        if turn_count <= 2:
            step_1_prompt = f"""
            ユーザーの回答: "{user_input}"
            あなたは親しい友人です。ユーザーが今日の感想を言いました。
            
            [タスク]
            1. まず共感してください（「そっか、楽しかったんだね！」「大変だったね」など）。
            2. 次に、「具体的にどこか詰まっちゃったところとか、エラーが出たところはあった？どんなことでも教えて！」と聞いてください。
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=step_1_prompt)])]
            )
            # Goalskill 분류: Part C, User 일지 답변 + AI 질문
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=user_input)
            save_daily_log(request.session_id, "user", user_input)
            await classify_and_save(session_id=request.session_id, sender="I", part="C", text=response.text)
            save_daily_log(request.session_id, "bot", response.text)
            return {"is_finished": False, "message": response.text}

        # Step 2: 어려웠던 점 듣고 -> 참고한 자료 물어보기 (★ 여기가 수정된 부분 ★)
        elif turn_count <= 4:
            step_2_prompt = f"""
            ユーザーの回答: "{user_input}"
            あなたは親しい友人です。ユーザーが「難しかった場所」や「エラー」について答えました。

            [制約事項 - **深掘り禁止(Do Not Dig Deeper)**]
            1. **即座に受容**: 回答が短くても、具体的でなくても、「そっか、そこが難しかったんだね」とすぐに受け入れてください。
            2. **質問禁止**: 「具体的にどんなエラー？」「どう解決した？」と**聞き返さないでください**。
            3. **次の話題へ**: すぐに「そういう時、何か参考にした動画とか資料とかはあったかな？」と次の質問に進んでください。
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=step_2_prompt)])]
            )
            # Goalskill 분류: Part C, User 일지 답변 + AI 질문
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=user_input)
            save_daily_log(request.session_id, "user", user_input)
            await classify_and_save(session_id=request.session_id, sender="I", part="C", text=response.text)
            save_daily_log(request.session_id, "bot", response.text)
            return {"is_finished": False, "message": response.text}

        # Step 3: 참고자료 듣고 -> 해결 여부/今の気持ち 물어보기
        elif turn_count <= 6:
            step_3_prompt = f"""
            ユーザーの回答: "{user_input}"
            あなたは親しい友人です。ユーザーが参考にした資料について答えました。
            
            [タスク]
            1. 「おお、それを見て頑張ったんだね！」と褒める。
            2. 最後に「その動画とかを見て、最終的に問題は解決できたかな？今はどんな気持ち？」と聞いて、締めくくりの準備をして。
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=step_3_prompt)])]
            )
            # Goalskill 분류: Part C, User 일지 답변 + AI 질문
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=user_input)
            save_daily_log(request.session_id, "user", user_input)
            await classify_and_save(session_id=request.session_id, sender="I", part="C", text=response.text)
            save_daily_log(request.session_id, "bot", response.text)
            return {"is_finished": False, "message": response.text}

        # Step 4: 마무리 및 저장
        else:
            final_prompt = f"""
            ユーザーの回答: "{user_input}"
            あなたは親しい友人です。これでインタビューは終了です。
            
            [タスク]
            1. 「よし！これで今日の日誌はバッチリだね！」と明るく締める。
            2. 「ゆっくり休んでね！」と労う。
            3. これ以上の質問はしない。
            """
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[types.Content(role="user", parts=[types.Part(text=final_prompt)])]
            )
            # Goalskill 분류: Part C, User 일지 마지막 답변 + AI 마무리
            await classify_and_save(session_id=request.session_id, sender="M", part="C", text=user_input)
            save_daily_log(request.session_id, "user", user_input)
            await classify_and_save(session_id=request.session_id, sender="I", part="C", text=response.text)
            save_daily_log(request.session_id, "bot", response.text)
            return {"is_finished": True, "message": response.text}

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# 3. 디버그 일지 생성 및 저장 (별 줍기 액션)
@router.post("/journal/generate")
async def generate_journal(request: GenerateJournalRequest):
    try:
        client = get_gemini_model()
        
        # [핵심 수정 1] 프롬프트 만들기 전에 '오늘이 며칠인지' 먼저 조회합니다.
        progress = curriculum_module.get_user_progress(request.session_id)
        current_day = progress['current_day'] if progress else 1
        
        # 대화 내역 문자열 변환
        conversation_text = "\n".join([f"{entry['role']}: {entry['text']}" for entry in request.conversation_history])
        
        # [핵심 수정 2] 프롬프트 예시(Day N)를 Day {current_day}로 변경하여 AI에게 정확한 숫자를 줍니다.
        prompt = f"""
        以下のユーザーとの会話ログを分析し、学習の振り返りデータを作成してください。
        
        [会話ログ]
        {conversation_text}
        
        [タスク]
        1. **summary_content**: 会話内容を元に、指定されたMarkdown形式で「デバッグ日誌」を作成する。
           形式例:
           #### 📅 **今日のデバッグ日誌 | Topic: ...**
           * **🔍状況把握:** ...
           * **📓 デバッグ:** ...
           * **💡PPT & YouTube:** ...
           * **🛠 解決策:** ...
           
        2. **understanding_score**: 今日のユーザーの理解度を 0〜100 の整数で評価する。
        3. **ai_feedback**: 学習内容に対する具体的なフィードバック（改善点やアドバイス）を親しい口調で書く。
        4. **praise_content**: 今日の頑張りを褒め称える言葉（「今日の称賛」）を書く。
        
        出力は必ず以下のJSONフォーマットのみにしてください：
        {{
            "summary_content": "...",
            "understanding_score": 85,
            "ai_feedback": "...",
            "praise_content": "..."
        }}
        """
        
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[types.Content(role="user", parts=[types.Part(text=prompt)])],
            config={"response_mime_type": "application/json"}
        )
        
        result = json.loads(response.text)
        
        # DB 저장
        saved = curriculum_module.save_debug_journal(
            request.session_id, 
            current_day, 
            result['summary_content'],
            result['understanding_score'],
            result['ai_feedback'],
            result['praise_content']
        )
        
        # Goalskill 분류: Part C, AI 일지 생성 결과 (summary + feedback + praise)
        journal_text = f"{result['summary_content']} {result['ai_feedback']} {result['praise_content']}"
        await classify_and_save(session_id=request.session_id, sender="I", part="C", text=journal_text)
        save_daily_log(request.session_id, "bot", journal_text)
        
        if not saved:
            raise Exception("DB Save Failed")
        
        # [핵심 수정: C파트 종료 시 즉시 다음 날짜(day + 1) 생성]
        from app.models.goalskill_module import create_result_row
        next_day = current_day + 1
        create_result_row(request.session_id, next_day)
        
        # カテゴリ別進捗率を再計算して保存
        try:
            curriculum_module.update_category_progress(request.session_id, current_day)
        except Exception as prog_e:
            logger.error(f"Category Progress Update Warning: {prog_e}")
        
        try:
            curriculum_module.update_progress_excel_and_pdf(request.session_id)
        except Exception as file_e:
            logger.error(f"File Update Warning: {file_e}")
            
        return {"status": "success", "day": next_day}

    except Exception as e:
        logger.error(str(e))
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/journal/view")
async def view_journal(request: GetJournalRequest):
    try:
        progress = curriculum_module.get_user_progress(request.session_id)
        current_day = progress['current_day'] if progress else 1
        
        # current_day는 일지 생성 후 이미 +1 된 상태이므로, 
        # 방금 작성한 일지는 current_day - 1 에 있음
        journal = curriculum_module.get_debug_journal(request.session_id, current_day)
        if not journal and current_day > 1:
            journal = curriculum_module.get_debug_journal(request.session_id, current_day - 1)
        
        if not journal:
            return {"found": False, "message": "日誌が見つかりません。"}
            
        return {"found": True, "data": journal}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
    
@router.get("/journal/history/{session_id}")
async def get_journal_history(session_id: str):
    try:
        history = curriculum_module.get_all_debug_journals(session_id)
        
        if not history:
            return {"count": 0, "data": []}
        
        # 날짜 포맷팅 (datetime -> "yyyy-MM-dd")
        data = []
        for row in history:
            date_str = row['created_at'].strftime('%Y-%m-%d') if row['created_at'] else "Unknown"
            data.append({
                "date": date_str,
                "summary_content": row['summary_content'],
                "ai_feedback": row['ai_feedback'],
                "praise_content": row['praise_content']
            })
            
        return {"count": len(data), "data": data}

    except Exception as e:
        logger.error(str(e))
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/progress/{session_id}")
async def get_progress(session_id: str):
    """ユーザーのカテゴリ別学習進捗率を取得"""
    try:
        data = curriculum_module.get_category_progress(session_id)
        return {"progress": data}
    except Exception as e:
        logger.error(f"Progress Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/plan/{session_id}")
async def get_plan(session_id: str):
    """ユーザーのカリキュラム計画（ガントチャート）を取得"""
    try:
        data = curriculum_module.get_curriculum_plan(session_id)
        return {"plan": data}
    except Exception as e:
        logger.error(f"Plan Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))
    

@file_router.get("/curriculum_pdf/{session_id}")
async def get_curriculum_pdf(session_id: str):
    """
    URL: /goal-skill-t/api/file/curriculum_pdf/{session_id}
    """
    # 파일이 위치한 디렉토리 경로
    base_dir = CURRICULUM_PDF_PATH
    file_name = f"curriculum_{session_id}.pdf"
    file_path = os.path.join(base_dir, file_name)
    
    # 파일 존재 여부 확인 후 전송
    if os.path.exists(file_path):
        return FileResponse(
            path=file_path,
            filename=file_name,
            media_type='application/pdf',
            content_disposition_type='inline'
        )
    else:
        raise HTTPException(status_code=404, detail="Curriculum PDF not found")
    

@file_router.get("/progress_pdf/{session_id}")
async def get_progress_pdf(session_id: str):
    """
    URL: /goal-skill-t/api/file/progress_pdf/{session_id}
    """
    # 진척도 PDF가 위치한 경로
    base_dir = PROGRESS_PDF_PATH
    file_name = f"progress_{session_id}.pdf"
    file_path = os.path.join(base_dir, file_name)
    
    # 파일 확인 및 전송 (inline으로 미리보기)
    if os.path.exists(file_path):
        return FileResponse(
            path=file_path,
            filename=file_name,
            media_type='application/pdf',
            content_disposition_type='inline' 
        )
    else:
        raise HTTPException(status_code=404, detail="Progress PDF not found")


# --------------------------------------------------------------------------
# AI カリキュラム生成
# --------------------------------------------------------------------------

class GenerateCurriculumRequest(BaseModel):
    session_id: str

@router.post("/generate")
async def generate_curriculum(request: GenerateCurriculumRequest):
    """
    ユーザープロフィールを基にAIでパーソナライズされたカリキュラムを生成します。
    既存のuser_curriculumを削除し、AI生成の新しいカリキュラムで置き換えます。
    """
    try:
        logger.info(f"[Generate] AI Curriculum request: {request.session_id}")
        result = await generate_ai_curriculum(request.session_id)

        if result["success"]:
            return {
                "status": "success",
                "message": result["message"],
                "item_count": result["item_count"]
            }
        else:
            return {
                "status": "error",
                "message": result["message"],
                "item_count": 0
            }
    except Exception as e:
        logger.error(f"[Generate] Error: {e}")
        return {
            "status": "error",
            "message": f"カリキュラム生成失敗: {str(e)}",
            "item_count": 0
        }
