"""backend.engines.dev_chatbot

개발챗봇 엔진 모듈.

구성
1. TOOL_ID / TOOL_INFO  – 엔진 메타 데이터 (레지스트리에 자동 수집)
2. prepare_context()     – Question ↔ VectorStore 유사도 검색 후 컨텍스트 생성
3. 벡터/파일 유틸        – index_pdf, delete_pdf, get_vector_store
4. FastAPI router        – PDF 업로드·조회·삭제 등 API

다른 엔진을 만들 때 이 파일을 템플릿으로 삼으면 일관된 구조를 유지할 수 있다.
"""

# General Chatbot engine configuration

TOOL_ID = "dev_chatbot"

TOOL_INFO = {
    "name": "개발챗봇",
    "description": "일반적인 챗봇입니다.",
    "system_prompt": (
        "당신은 다목적 AI 어시스턴트입니다. 사용자의 질문에 대해 PDF 문서 분석 및 실시간 웹 검색을 통해 가장 신뢰할 수 있는 정보를 찾아 제공해야 합니다. "
        "1) 질문과 관련된 내부·외부 PDF 자료가 있으면 우선적으로 내용을 요약·분석하여 근거와 함께 답변하세요. "
        "2) 추론·계산·코드 예시·표·수식이 도움이 되면 적극 활용하세요. "
        "3) 답변의 신뢰도를 높이기 위해 항상 출처(페이지 번호, 링크 등)를 명시하고, 확인되지 않은 정보는 가정임을 분명히 밝히세요. "
        "4) 모호한 요청이나 추가 정보가 필요한 경우에는 명확한 follow-up 질문을 통해 요구 사항을 파악한 뒤 답변하세요. "
        "5) 사실과 다른 정보를 임의로 생성하지 마세요. 필요한 정보가 없을 경우 솔직히 설명하고 가능한 대안을 제시합니다."
        "6) 한국어로 대답해주세요."
    )
}

from typing import Tuple, List
import os
from langchain.schema import Document

# get_vector_store 함수는 app.py 에 정의되어 주입받는다.

import urllib.parse
from fastapi import APIRouter, UploadFile, File
from fastapi.responses import StreamingResponse
from datetime import datetime

# -------------------------------------------------
# 벡터스토어 및 PDF 색인/삭제 기능
# -------------------------------------------------
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
UPLOAD_DIR = os.path.join(ROOT_DIR, "uploads")
os.makedirs(UPLOAD_DIR, exist_ok=True)

# 벡터스토어 기능 비활성화(GPT 전용 모드)
def get_vector_store():
    return None

# PDF 처리 기능 비활성화
try:
    from langchain.text_splitter import RecursiveCharacterTextSplitter
except ModuleNotFoundError:
    from langchain_text_splitters import RecursiveCharacterTextSplitter

try:
    import pdfplumber  # type: ignore
except ImportError:
    pdfplumber = None


def delete_pdf(filename: str):
    file_path = os.path.join(UPLOAD_DIR, filename)
    if os.path.exists(file_path):
        os.remove(file_path)
    vectordb = get_vector_store()
    try:
        vectordb.delete(where={"source": filename, "tool": TOOL_ID})
    except Exception:
        pass

# -------------------------------------------------
# FastAPI Router (PDF 업로드/조회/삭제)
# -------------------------------------------------
router = APIRouter()

@router.post("/upload_pdf")
async def upload_pdf(files: List[UploadFile] = File(...)):
    saved_files = []
    for up in files:
        if not up.filename.lower().endswith(".pdf"):
            continue
        filename = f"{datetime.now().strftime('%Y%m%d%H%M%S%f')}_{up.filename}"
        file_path = os.path.join(UPLOAD_DIR, filename)
        with open(file_path, "wb") as f:
            f.write(await up.read())
        try:
            if pdfplumber is None:
                # pdfplumber 미설치 시 인덱싱 건너뜀
                return

            filename = os.path.basename(file_path)
            docs = []
            saved_files.append(filename)
        except Exception as e:
            return {"status": "error", "message": str(e)}
    return {"status": "success", "files": saved_files}

@router.get("/files")
async def list_files():
    pdfs = [f for f in os.listdir(UPLOAD_DIR) if f.lower().endswith('.pdf')]
    return {"files": pdfs}

@router.get("/file_content")
async def get_file_content(filename: str):
    file_path = os.path.join(UPLOAD_DIR, filename)
    if not os.path.isfile(file_path):
        return {"status": "error", "message": "파일을 찾을 수 없습니다."}
    try:
        # Assuming PDFPlumberLoader is available or needs to be imported
        # from langchain.document_loaders import PDFPlumberLoader
        # loader = PDFPlumberLoader(file_path)
        # docs = loader.load()
        # text = "\n\n".join(d.page_content for d in docs)
        # return {"status": "success", "content": text}
        # Placeholder for actual PDF content extraction if PDFPlumberLoader is not available
        return {"status": "success", "content": "PDF content extraction is not implemented in this engine."}
    except Exception as e:
        return {"status": "error", "message": str(e)}

@router.get("/pdf")
async def serve_pdf(filename: str):
    # 1차: uploads 디렉터리
    file_path = os.path.join(UPLOAD_DIR, filename)
    # 2차: scripts/gxp 원본 디렉터리 (GxP 문서용)
    if not os.path.isfile(file_path):
        alt_dir = os.path.join(ROOT_DIR, "scripts", "gxp")
        file_path = os.path.join(alt_dir, filename)
    if not os.path.isfile(file_path):
        return {"status":"error","message":"파일을 찾을 수 없습니다."}
    file_stream = open(file_path, "rb")
    safe_name = urllib.parse.quote(filename)
    headers = {"Content-Disposition": f'inline; filename="{safe_name}"'}
    return StreamingResponse(file_stream, media_type="application/pdf", headers=headers)

@router.delete("/file")
async def remove_file(filename: str):
    try:
        delete_pdf(filename)
        return {"status": "success"}
    except Exception as e:
        return {"status": "error", "message": str(e)}


def prepare_context(message: str, knowledge_mode: str, vectordb) -> Tuple[str, List[Document]]:
    """사용자 메시지에 대한 벡터 검색 컨텍스트와 문서 리스트를 반환합니다.
    vectordb: langchain_chroma.Chroma 인스턴스 (지연 로드)
    """
    context_text = ""
    retrieved_docs: List[Document] = []
    if not vectordb or vectordb._collection.count() == 0:
        return context_text, retrieved_docs

    # dev_chatbot 문서만 대상으로 검색
    docs_with_scores = vectordb.similarity_search_with_score(
        message,
        k=4,
        filter={"tool": TOOL_ID}
    )
    score_threshold = 0.9
    filtered = [d for d, s in docs_with_scores if s is not None and s < score_threshold]
    retrieved_docs = filtered if filtered else [d for d, _ in docs_with_scores[:2]]

    if not retrieved_docs:
        return context_text, []

    # 페이지별 그룹화 후 상위 2페이지만 사용
    page_groups = {}
    for d in retrieved_docs:
        src = d.metadata.get("source", "")
        pg = d.metadata.get("page_number", "")
        key = (src, pg)
        page_groups.setdefault(key, []).append(d.page_content)

    selected_pages = list(page_groups.keys())[:2]
    snippets = []
    for (src, pg) in selected_pages:
        texts = page_groups[(src, pg)]
        joined = "\n".join(texts)
        snippet_txt = joined[:1500]
        header = f"[출처:{src} p{pg}]"
        snippets.append(header + "\n" + snippet_txt)

    context_text = "\n\n[관련 문서 발췌]\n" + "\n---\n".join(snippets)

    # retrieved_docs 축소
    retrieved_docs = [d for d in retrieved_docs if (d.metadata.get("source",""), d.metadata.get("page_number","")) in selected_pages]
    return context_text, retrieved_docs 