# app/routers/studyplan_router.py

from fastapi import APIRouter, HTTPException
from fastapi.responses import FileResponse
from app.schemas import studyplan_schema
from app.models import studyplan_module
from app.services import studyplan_questions
from app.services.studyplan_excel_generator import fill_excel_with_data
from app.core.config import logger
import os
import shutil
import subprocess
from pathlib import Path

router = APIRouter(
    prefix="/goal-skill-t/api/studyplan",
    tags=["StudyPlan"]
)


@router.post("/start")
async def start_studyplan(request: studyplan_schema.StartStudyPlanRequest):
    """
    학습계획표 시작 - 첫 번째 질문 반환
    
    Returns:
        - question_number: 질문 번호 (1)
        - question_text: 질문 내용
        - current: 현재 질문 번호 (1)
        - total: 전체 질문 수 (5)
    """
    try:
        # 전체 질문 가져오기
        all_questions = studyplan_questions.get_questions()
        
        if not all_questions:
            raise HTTPException(status_code=500, detail="No questions available")
        
        # 첫 번째 질문
        first_question = all_questions[0]
        
        logger.info(f"StudyPlan Started for session: {request.session_id}")
        
        return {
            "question_number": first_question["question_number"],
            "question_text": first_question["text"],
            "reason": first_question.get("reason", ""),  # 이유 추가
            "current": 1,
            "total": len(all_questions)
        }
        
    except Exception as e:
        logger.error(f"StudyPlan Start Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/answer")
async def answer_studyplan(request: studyplan_schema.AnswerStudyPlanRequest):
    """
    학습계획표 답변 제출
    
    Returns:
        - status: "continue" (계속) or "completed" (완료)
        - next_question: 다음 질문 (continue인 경우)
        - result: 완료 메시지 (completed인 경우)
    """
    try:
        # 현재 질문 정보 가져오기
        current_question = studyplan_questions.get_question_by_number(request.question_number)
        
        if not current_question:
            raise HTTPException(status_code=404, detail="Question not found")
        
        # 답변 저장 (studyplan_log)
        studyplan_module.save_studyplan_question(
            session_id=request.session_id,
            question_number=request.question_number,
            question_text=current_question["text"],
            answer_text=request.answer_text
        )
        
        # 현재까지 답변한 질문 수 확인
        answered_count = request.question_number
        total_questions = len(studyplan_questions.get_questions())
        
        logger.info(f"StudyPlan Progress: {answered_count}/{total_questions} for session {request.session_id}")
        
        # 모든 질문 완료 시
        if answered_count >= total_questions:
            # 답변들을 가져와서 최종 결과 생성 및 PDF 생성
            pdf_path = await process_final_result(request.session_id)
            
            return {
                "status": "completed",
                "message": "学習計画表の入力が完了しました！\n\n学習計画表のPDFを生成しました。",
                "pdf_url": f"/studyplan/pdf/{request.session_id}" if pdf_path else None
            }
        
        # 다음 질문 반환
        all_questions = studyplan_questions.get_questions()
        next_question = all_questions[answered_count]  # 다음 질문 (0-based index)
        
        return {
            "status": "continue",
            "next_question": {
                "question_number": next_question["question_number"],
                "question_text": next_question["text"],
                "reason": next_question.get("reason", ""),  # 이유 추가
                "current": answered_count + 1,
                "total": total_questions
            }
        }
        
    except Exception as e:
        logger.error(f"StudyPlan Answer Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))


async def process_final_result(session_id: str) -> str:
    """
    모든 질문 완료 시 최종 결과 처리
    - studyplan_log에서 답변들 가져오기
    - 답변 파싱 (데이터베이스 스키마에 맞게)
    - studyplan_Output에 저장
    - user_profile_summary에 동기화
    - BASE 엑셀 복사 → 데이터 채우기 → PDF 생성
    
    Returns:
        PDF 파일 경로
    """
    try:
        import re
        
        # 1. studyplan_log에서 답변들 가져오기
        answers = studyplan_module.get_answers_by_session(session_id)
        
        if len(answers) != 5:
            logger.error(f"Expected 5 answers, but got {len(answers)} for session {session_id}")
            logger.error(f"Answers: {answers}")
            raise HTTPException(status_code=400, detail=f"Not all questions answered. Got {len(answers)} answers instead of 5.")
        
        # 2. AI를 사용하여 답변 파싱 (더 정확한 데이터 추출)
        parsed_data = await parse_answers_with_ai(answers)
        
        total_duration = parsed_data.get('total_duration', answers[0]["answer_text"].strip())
        current_status = parsed_data.get('current_status', answers[1]["answer_text"].strip())
        weekday_hours = parsed_data.get('weekday_hours', extract_number(answers[2]["answer_text"].strip()))
        weekend_hours = parsed_data.get('weekend_hours', extract_number(answers[3]["answer_text"].strip()))
        preferred_time_slot = parsed_data.get('preferred_time_slot', answers[4]["answer_text"].strip())
        
        # 로그로 확인 (상세 로그 추가)
        logger.info(f"=== StudyPlan Parsing Result ===")
        logger.info(f"Original answers: {[a['answer_text'] for a in answers]}")
        logger.info(f"Parsed data: duration={total_duration}, status={current_status}")
        logger.info(f"Parsed weekday_hours={weekday_hours} (type: {type(weekday_hours)})")
        logger.info(f"Parsed weekend_hours={weekend_hours} (type: {type(weekend_hours)})")
        logger.info(f"Parsed time_slot={preferred_time_slot}")
        logger.info(f"================================")
        
        # 3. studyplan_Output에 저장
        studyplan_module.save_studyplan_output(
            session_id=session_id,
            total_duration=total_duration,
            current_status=current_status,
            weekday_hours=weekday_hours,
            weekend_hours=weekend_hours,
            preferred_time_slot=preferred_time_slot
        )
        
        # 4. user_profile_summary에 동기화
        studyplan_module.sync_to_user_profile_summary(
            session_id=session_id,
            studyplan_duration=total_duration,
            studyplan_status=current_status,
            studyplan_weekday_hours=weekday_hours,
            studyplan_weekend_hours=weekend_hours,
            studyplan_time_slot=preferred_time_slot
        )
        
        # 5. BASE 엑셀 복사 → 데이터 채우기 → PDF 생성
        # 이미 AI로 파싱한 값을 직접 사용 (원본 텍스트 보장)
        pdf_path = await generate_studyplan_schedule(
            session_id=session_id,
            total_duration=total_duration,
            current_status=current_status,
            weekday_hours=weekday_hours,
            weekend_hours=weekend_hours,
            preferred_time_slot=preferred_time_slot  # 원본 텍스트 (예: "夕方")
        )
        
        logger.info(f"StudyPlan Final Result Processed for session: {session_id}, PDF: {pdf_path}")
        
        return pdf_path
        
    except Exception as e:
        logger.error(f"StudyPlan Final Result Error: {e}")
        raise e


async def parse_answers_with_ai(answers: list) -> dict:
    """
    AI를 사용하여 5개의 답변을 분석하고 구조화된 데이터 추출
    
    Args:
        answers: 답변 리스트 (question_number, question_text, answer_text 포함)
    
    Returns:
        {
            'total_duration': str,  # 예: "6ヶ月"
            'current_status': str,  # 예: "会社員"
            'weekday_hours': int,   # 예: 1
            'weekend_hours': int,   # 예: 3
            'preferred_time_slot': str  # 예: "夕方"
        }
    """
    try:
        from app.core.config import get_gemini_model
        import json
        
        # 질문과 답변을 텍스트로 구성
        qa_text = ""
        for i, answer in enumerate(answers, 1):
            qa_text += f"Q{i}: {answer['question_text']}\nA{i}: {answer['answer_text']}\n\n"
        
        prompt = f"""
以下の5つの質問と回答を分析して、構造化されたデータを抽出してください。

{qa_text}

以下のJSON形式で返答してください。数字は必ず整数で返してください。
{{
    "total_duration": "目標期間（例: 6ヶ月）",
    "current_status": "現在の状態（例: 会社員）",
    "weekday_hours": 平日の学習時間（整数、例: 1）,
    "weekend_hours": 週末の学習時間（整数、例: 3）,
    "preferred_time_slot": "希望時間帯（例: 夕方、夜間など、元のテキストそのまま）"
}}

重要:
- weekday_hours: Q3の回答から平日（月〜金）の学習時間を抽出してください。ユーザーが「1時間」と言ったら1、「2時間」と言ったら2です。
- weekend_hours: Q4の回答から週末の学習時間を抽出してください。ユーザーが「3時間」と言ったら3、「6時間」と言ったら6です。
- weekday_hoursとweekend_hoursは必ず整数で返してください。推測や補完をせず、ユーザーが実際に言った数字だけを返してください。
- preferred_time_slotはユーザーが入力した元のテキストをそのまま返してください（例: "夕方"、"夜間"など）
- JSONのみを返答してください。他の説明は不要です。
"""
        
        client = get_gemini_model()
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=prompt,
            config={
                "temperature": 0.1,  # 낮은 temperature로 일관성 확보
                "response_mime_type": "application/json"
            }
        )
        
        # JSON 파싱
        result_text = response.text.strip()
        # JSON 코드 블록 제거 (```json ... ```)
        if result_text.startswith("```"):
            result_text = result_text.split("```")[1]
            if result_text.startswith("json"):
                result_text = result_text[4:]
            result_text = result_text.strip()
        
        parsed = json.loads(result_text)
        
        # 검증 및 기본값 설정
        parsed['weekday_hours'] = int(parsed.get('weekday_hours', 0))
        parsed['weekend_hours'] = int(parsed.get('weekend_hours', 0))
        
        return parsed
        
    except Exception as e:
        logger.error(f"AI parsing error: {e}, falling back to simple extraction")
        # AI 파싱 실패 시 기본 방법 사용
        return {
            'total_duration': answers[0]["answer_text"].strip(),
            'current_status': answers[1]["answer_text"].strip(),
            'weekday_hours': extract_number(answers[2]["answer_text"].strip()),
            'weekend_hours': extract_number(answers[3]["answer_text"].strip()),
            'preferred_time_slot': answers[4]["answer_text"].strip()
        }


def extract_number(text: str) -> int:
    """
    텍스트에서 숫자 추출
    예: "2時間" → 2, "6時間" → 6
    """
    import re
    # 숫자만 추출 (첫 번째 숫자)
    match = re.search(r'\d+', text)
    if match:
        return int(match.group())
    return 0  # 숫자가 없으면 0 반환


def convert_excel_to_pdf(excel_path: str, output_dir: str) -> str:
    """
    LibreOffice를 사용하여 Excel 파일을 PDF로 변환합니다.
    
    Args:
        excel_path: 엑셀 파일 경로
        output_dir: PDF 출력 디렉토리
    
    Returns:
        생성된 PDF 파일 경로
    """
    try:
        # LibreOffice 변환 명령
        # 인쇄 영역 설정을 따르도록 기본 변환 사용
        logger.info(f"Converting Excel to PDF: {excel_path}")
        logger.info(f"Excel file exists: {os.path.exists(excel_path)}")
        
        # SinglePageSheets 옵션으로 각 시트를 한 페이지에 맞춤
        # 인쇄 영역 설정을 따르도록 함
        command = [
            "libreoffice",
            "--headless",
            "--nodefault",
            "--nolockcheck",
            "--convert-to",
            'pdf:calc_pdf_Export:{"SinglePageSheets":{"type":"boolean","value":"true"}}',
            "--outdir",
            output_dir,
            excel_path
        ]
        logger.info(f"LibreOffice command: {' '.join(command)}")
        result = subprocess.run(command, capture_output=True, text=True, check=True, timeout=30)
        logger.info(f"LibreOffice PDF conversion stdout: {result.stdout}")
        if result.stderr:
            logger.warning(f"LibreOffice PDF conversion stderr: {result.stderr}")

        pdf_filename = os.path.splitext(os.path.basename(excel_path))[0] + ".pdf"
        pdf_path = os.path.join(output_dir, pdf_filename)
        
        if not os.path.exists(pdf_path):
            raise FileNotFoundError(f"PDF file was not created: {pdf_path}")
        
        return pdf_path
        
    except FileNotFoundError as e:
        if "libreoffice" in str(e) or "not found" in str(e).lower():
            logger.error("ERROR: 'libreoffice' command not found. Is LibreOffice installed and in the system's PATH?")
        else:
            logger.error(f"ERROR: {e}")
        raise
    except subprocess.CalledProcessError as e:
        logger.error(f"ERROR: LibreOffice conversion failed. Return code: {e.returncode}")
        logger.error(f"Stderr: {e.stderr}")
        raise
    except subprocess.TimeoutExpired:
        logger.error("ERROR: LibreOffice conversion timed out")
        raise
    except Exception as e:
        logger.error(f"An unexpected error occurred during PDF conversion: {e}")
        raise


async def generate_studyplan_schedule(session_id: str, total_duration: str, current_status: str,
                                      weekday_hours: int, weekend_hours: int, preferred_time_slot: str) -> str:
    """
    학습계획표 시간표 엑셀 파일 생성 및 PDF 변환
    BASE 엑셀 템플릿을 복사해서 유저 데이터를 채운 후 LibreOffice로 PDF 변환
    
    Args:
        session_id: 세션 ID
        total_duration: 목표 기간
        current_status: 현재 상태
        weekday_hours: 평일 학습 시간
        weekend_hours: 주말 학습 시간
        preferred_time_slot: 선호 시간대
    
    Returns:
        PDF 파일 경로
    """
    try:
        # 파일 저장 디렉토리
        base_dir = Path(__file__).resolve().parent.parent.parent
        studyplan_dir = base_dir / "studyplan_files"
        studyplan_dir.mkdir(exist_ok=True)
        
        # BASE 엑셀 템플릿 경로
        base_excel_path = studyplan_dir / "base_studyplan_template.xlsx"
        
        # BASE 엑셀이 없으면 에러
        if not base_excel_path.exists():
            logger.error(f"BASE 엑셀 템플릿이 없습니다: {base_excel_path}")
            logger.error("먼저 'python create_base_excel.py'를 실행하여 BASE 엑셀을 생성하세요.")
            return None
        
        # 유저별 엑셀 파일 경로 (BASE를 복사)
        user_excel_path = studyplan_dir / f"studyplan_{session_id}.xlsx"
        shutil.copy2(base_excel_path, user_excel_path)
        
        # 유저 데이터로 엑셀 채우기
        fill_excel_with_data(
            excel_path=str(user_excel_path),
            total_duration=total_duration,
            current_status=current_status,
            weekday_hours=weekday_hours,
            weekend_hours=weekend_hours,
            preferred_time_slot=preferred_time_slot
        )
        
        # LibreOffice를 사용하여 PDF 변환
        pdf_path = convert_excel_to_pdf(
            excel_path=str(user_excel_path),
            output_dir=str(studyplan_dir)
        )
        
        logger.info(f"StudyPlan Excel generated: {user_excel_path}")
        logger.info(f"StudyPlan Excel file size: {os.path.getsize(user_excel_path)} bytes")
        logger.info(f"StudyPlan PDF generated: {pdf_path}")
        logger.info(f"StudyPlan PDF file size: {os.path.getsize(pdf_path)} bytes")
        
        # Excel 파일을 직접 확인할 수 있도록 경로 로그 출력
        logger.info(f"=== Excel file available for manual check: {user_excel_path} ===")
        
        return pdf_path
        
    except Exception as e:
        logger.error(f"StudyPlan PDF Generation Error: {e}")
        import traceback
        logger.error(traceback.format_exc())
        # PDF 생성 실패해도 메인 로직은 계속 진행
        return None


@router.get("/pdf/{session_id}")
async def get_studyplan_pdf(session_id: str):
    """
    학습계획표 PDF 다운로드/미리보기
    """
    try:
        import urllib.parse
        base_dir = Path(__file__).resolve().parent.parent.parent
        pdf_path = base_dir / "studyplan_files" / f"studyplan_{session_id}.pdf"
        
        if not pdf_path.exists():
            raise HTTPException(status_code=404, detail="PDF file not found")
        
        from fastapi.responses import Response
        
        # PDF 파일 읽기
        with open(pdf_path, "rb") as f:
            pdf_content = f.read()
        
        # 파일명 인코딩 (RFC 5987: UTF-8 인코딩 사용)
        filename_utf8 = f"学習計画表_{session_id}.pdf"
        filename_encoded = urllib.parse.quote(filename_utf8, safe='')
        
        # 적절한 헤더와 함께 반환 (브라우저에서 미리보기 가능하도록)
        # 캐싱 방지를 위해 파일 수정 시간 기반 ETag 사용
        import time
        file_mtime = os.path.getmtime(pdf_path)
        etag = f'"{int(file_mtime)}"'
        
        return Response(
            content=pdf_content,
            media_type="application/pdf",
            headers={
                # ASCII 파일명 (fallback)
                "Content-Disposition": f'inline; filename="studyplan_{session_id}.pdf"; filename*=UTF-8\'\'{filename_encoded}',
                "Content-Type": "application/pdf",
                "Cache-Control": "no-cache, no-store, must-revalidate",  # 캐싱 방지
                "Pragma": "no-cache",
                "Expires": "0",
                "ETag": etag,
                "Access-Control-Allow-Origin": "*"  # CORS 허용
            }
        )
        
    except Exception as e:
        logger.error(f"StudyPlan PDF Download Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/admin-base-curriculum")
async def get_admin_base_curriculum():
    """
    학습계획표 양식 PDF 파일 서빙
    /home/air/goalskill_t/back/studyplan_files/admin_base_curriculum.pdf
    """
    try:
        base_dir = Path(__file__).resolve().parent.parent.parent
        pdf_path = base_dir / "studyplan_files" / "admin_base_curriculum.pdf"
        
        logger.info(f"Admin Base Curriculum PDF Request: path={pdf_path}")
        
        # 파일 존재 확인
        if not pdf_path.exists():
            logger.error(f"Admin Base Curriculum PDF not found: {pdf_path}")
            raise HTTPException(status_code=404, detail="Admin base curriculum PDF not found")
        
        logger.info(f"Admin Base Curriculum PDF found: {pdf_path}")
        
        # PDF 파일 읽기
        with open(pdf_path, "rb") as f:
            pdf_content = f.read()
        
        # 파일명 인코딩 (ASCII fallback 사용)
        import urllib.parse
        filename_ascii = "admin_base_curriculum.pdf"
        filename_utf8 = "学習計画表_基本フォーム.pdf"
        filename_encoded = urllib.parse.quote(filename_utf8, safe='')
        
        from fastapi.responses import Response
        
        return Response(
            content=pdf_content,
            media_type="application/pdf",
            headers={
                "Content-Disposition": f'inline; filename="{filename_ascii}"; filename*=UTF-8\'\'{filename_encoded}',
                "Content-Type": "application/pdf",
                "Cache-Control": "no-cache, no-store, must-revalidate",
                "Pragma": "no-cache",
                "Expires": "0",
                "Access-Control-Allow-Origin": "*"
            }
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Admin Base Curriculum PDF Error: {e}")
        raise HTTPException(status_code=500, detail=str(e))
